应用服务无法通过本地锁来控制并发,故需要分布式锁来控制
Jedis 是 Redis 的 Java 实现的客户端,其 API 提供了比较全面的 Redis 命令,的支持;Redisson 实现了分布式和可扩展的 Java 数据结构,和 Jedis 相比,功能,较为简单,不支持字符串操作,不支持排序、事务、管道、分区等 Redis 特性。
Redisson 的宗旨是促进使用者对 Redis 的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
public static Boolean lock2(Jedis jedis,String key,String requestId){
String result = null;
long start = System.currentTimeMillis();
do {
result = jedis.set(key, requestId, "NX", "EX", expireTime);
if(!"OK".equals(result)){
return true;
}
long cost = System.currentTimeMillis() - start;
if(cost > 10){
return false;
}
} while (true);
}
public static Boolean unLock2(Jedis jedis,String key,String requestId){
Long result = null;
if(requestId.equals(jedis.get(key))){
result = jedis.del(key);
}
return result != null && result == 0;
}
不可重入,无续期 过期时间,当A线程获取锁执行后,到达过期,B线程可以获取到锁
public static Long lock2(Jedis jedis,String key,String requestId,Long expireTime){
Long result = null;
do {
if(!jedis.exists(key)){//判断是否存在,若存在则代表有客户端获取到锁
jedis.hset(key, requestId,"1");//写入hash表中,1代表锁过一次
jedis.pexpire(key,expireTime);
break;
}
if(jedis.hexists(key,requestId)){//判断获取到锁的客户端是否是自己
jedis.hincrBy(key,requestId,1L);//若是自己则锁的次数加1
jedis.pexpire(key,expireTime);//刷新锁的时间
break;
}
result = jedis.pttl(key);//未获取到锁,则返回当前锁的过期时间
} while (result != null);
return result;
}
public static Integer unLock2(Jedis jedis,String key,String requestId,Long expireTime){
if(!jedis.exists(key)){//判断是否存在,不存在则已解锁
return 1;
}
if(!jedis.hexists(key,requestId)){//该请求是否持有锁,不存在则是其他请求持有锁
return null;
}
if(jedis.hincrBy(key,requestId,-1) > 0){//减一后还大于0,则该请求还是持有锁,有过多次加锁
jedis.pexpire(key,expireTime);
return 0;
}else {//解锁次数大于等于加锁次数,释放锁
jedis.del(key);
return 1;
}
}
无续期机制
"if (redis.call(‘exists‘, KEYS[1]) == 0) then " +
"redis.call(‘hset‘, KEYS[1], ARGV[2], 1); " +
"redis.call(‘pexpire‘, KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call(‘hexists‘, KEYS[1], ARGV[2]) == 1) then " +
"redis.call(‘hincrby‘, KEYS[1], ARGV[2], 1); " +
"redis.call(‘pexpire‘, KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call(‘pttl‘, KEYS[1]);",
通过封装在lua脚本中发送给redis,保证这段复杂业务逻辑执行的原子性
"if (redis.call(‘exists‘, KEYS[1]) == 0) then " +
"redis.call(‘publish‘, KEYS[2], ARGV[1]); " +
"return 1; " +
"end;" +
"if (redis.call(‘hexists‘, KEYS[1], ARGV[3]) == 0) then " +
"return nil;" +
"end; " +
"local counter = redis.call(‘hincrby‘, KEYS[1], ARGV[3], -1); " +
"if (counter > 0) then " +
"redis.call(‘pexpire‘, KEYS[1], ARGV[2]); " +
"return 0; " +
"else " +
"redis.call(‘del‘, KEYS[1]); " +
"redis.call(‘publish‘, KEYS[2], ARGV[1]); " +
"return 1; "+
"end; " +
"return nil;",
每次都对数据结构中的那个加锁次数减1,如果发现加锁次数是0了,说明这个客户端已经不再持有锁了
"if (redis.call(‘hexists‘, KEYS[1], ARGV[2]) == 1) then " +
"redis.call(‘pexpire‘, KEYS[1], ARGV[1]); " +
"return 1; " +
"end; " +
"return 0;"
缺点:一个服务实例在master上获取到锁,如果master和salve同步的过程中,master宕机了,salve还没有同步到这把锁,就被切换成了master,另一个服务实例在新的master上获取到一把新锁,这时候就会出现俩台服务实例都持有锁
原文:https://www.cnblogs.com/lantuanqing/p/14365338.html