redis作为分布式锁的疑问

熏肉大饼 2017-12-23 04:57:10
代码是分布式锁 常见的实现方式,利用setnx,getSet设置和获取


/* */
/* */ public class JedisLock
/* */ {
/* */ RedisProxy jedis;
/* */ String lockKey;
/* 20 */ int expireMsecs = 60000;
/* */
/* 25 */ int timeoutMsecs = 10000;
/* */
/* 27 */ boolean locked = false;
/* */
/* */ public JedisLock(RedisProxy jedis, String lockKey)
/* */ {
/* 37 */ this.jedis = jedis;
/* 38 */ this.lockKey = lockKey;
/* */ }
/* */
/* */ public JedisLock(RedisProxy jedis, String lockKey, int timeoutMsecs)
/* */ {
/* 51 */ this(jedis, lockKey);
/* 52 */ this.timeoutMsecs = timeoutMsecs;
/* */ }
/* */
/* */ public JedisLock(RedisProxy jedis, String lockKey, int timeoutMsecs, int expireMsecs)
/* */ {
/* 67 */ this(jedis, lockKey, timeoutMsecs);
/* 68 */ this.expireMsecs = expireMsecs;
/* */ }
/* */
/* */ public String getLockKey()
/* */ {
/* 75 */ return this.lockKey;
/* */ }
/* */
/* */ public synchronized boolean acquire()
/* */ throws InterruptedException
/* */ {
/* 87 */ return acquire(this.jedis);
/* */ }
/* */
/* */ public synchronized boolean acquire(RedisProxy jedis)
/* */ throws InterruptedException
/* */ {
/* 99 */ int timeout = this.timeoutMsecs;
/* 100 */ while (timeout >= 0) {
/* 101 */ long expires = System.currentTimeMillis() + this.expireMsecs + 1L;
/* 102 */ String expiresStr = String.valueOf(expires);
/* */
/* 104 */ if (jedis.setnx(this.lockKey, expiresStr).longValue() == 1L)
/* */ {
/* 106 */ this.locked = true;
/* 107 */ return true;
/* */ }
/* */
/* 110 */ String currentValueStr = jedis.get(this.lockKey);
/* 111 */ if ((currentValueStr != null) && (Long.parseLong(currentValueStr) < System.currentTimeMillis()))
/* */ {
/* 115 */ String oldValueStr = jedis.getSet(this.lockKey, expiresStr);
/* */
/* 118 */ if ((oldValueStr != null) && (oldValueStr.equals(currentValueStr)))
/* */ {
/* 121 */ this.locked = true;
/* 122 */ return true;
/* */ }
/* */ }
/* 125 */ timeout -= 100;
/* 126 */ Thread.sleep(100L);
/* */ }
/* 128 */ return false;
/* */ }
/* */
/* */ public synchronized void release()
/* */ {
/* 135 */ release(this.jedis);
/* */ }
/* */
/* */ public synchronized void release(RedisProxy jedis)
/* */ {
/* 142 */ if (this.locked) {
/* 143 */ jedis.del(this.lockKey);
/* 144 */ this.locked = false;
/* */ }
/* */ }
/* */ }


本人的疑问点在于:
当前时间2017-12-23 00:00:00:000
如果a线程即将过期未过期(过期时间2017-12-23 00:00:00:100),获取分布式锁,

此时b线程进入,时间为2017-12-23 00:00:00:200并且(a线程未执行完毕),b线程发现锁的时间已经过期,竞争最后获取锁

c线程在等待时,a线程执行完毕,unlock操作删除lockKey,c也获取锁
那么就导致两个线程同时执行同一个的可能性
(有人会说a线程设置的过期时间太短,你可以理解为a线程过期时间很长,但是a线程执行时其他数据锁表了,一直阻塞导致当前时间超过执行时间的可能性)
...全文
918 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
nwpulei 2018-01-19
  • 打赏
  • 举报
回复
看下 redlock
X元素 2018-01-16
  • 打赏
  • 举报
回复
从思路上楼主试试改一下, redis 封装成简单的两个函数:lock unlock; 保证每次使用全局redis锁的时候,调用lock ,调用完成保证unlock执行; 这样是不是能解决你的问题了?
kfsykk 2018-01-11
  • 打赏
  • 举报
回复
引用 楼主 w5167839 的回复:
代码是分布式锁 常见的实现方式,利用setnx,getSet设置和获取

/*     */ 
/*     */ public class JedisLock
/*     */ {
/*     */   RedisProxy jedis;
/*     */   String lockKey;
/*  20 */   int expireMsecs = 60000;
/*     */ 
/*  25 */   int timeoutMsecs = 10000;
/*     */ 
/*  27 */   boolean locked = false;
/*     */ 
/*     */   public JedisLock(RedisProxy jedis, String lockKey)
/*     */   {
/*  37 */     this.jedis = jedis;
/*  38 */     this.lockKey = lockKey;
/*     */   }
/*     */ 
/*     */   public JedisLock(RedisProxy jedis, String lockKey, int timeoutMsecs)
/*     */   {
/*  51 */     this(jedis, lockKey);
/*  52 */     this.timeoutMsecs = timeoutMsecs;
/*     */   }
/*     */ 
/*     */   public JedisLock(RedisProxy jedis, String lockKey, int timeoutMsecs, int expireMsecs)
/*     */   {
/*  67 */     this(jedis, lockKey, timeoutMsecs);
/*  68 */     this.expireMsecs = expireMsecs;
/*     */   }
/*     */ 
/*     */   public String getLockKey()
/*     */   {
/*  75 */     return this.lockKey;
/*     */   }
/*     */ 
/*     */   public synchronized boolean acquire()
/*     */     throws InterruptedException
/*     */   {
/*  87 */     return acquire(this.jedis);
/*     */   }
/*     */ 
/*     */   public synchronized boolean acquire(RedisProxy jedis)
/*     */     throws InterruptedException
/*     */   {
/*  99 */     int timeout = this.timeoutMsecs;
/* 100 */     while (timeout >= 0) {
/* 101 */       long expires = System.currentTimeMillis() + this.expireMsecs + 1L;
/* 102 */       String expiresStr = String.valueOf(expires);
/*     */ 
/* 104 */       if (jedis.setnx(this.lockKey, expiresStr).longValue() == 1L)
/*     */       {
/* 106 */         this.locked = true;
/* 107 */         return true;
/*     */       }
/*     */ 
/* 110 */       String currentValueStr = jedis.get(this.lockKey);
/* 111 */       if ((currentValueStr != null) && (Long.parseLong(currentValueStr) < System.currentTimeMillis()))
/*     */       {
/* 115 */         String oldValueStr = jedis.getSet(this.lockKey, expiresStr);
/*     */ 
/* 118 */         if ((oldValueStr != null) && (oldValueStr.equals(currentValueStr)))
/*     */         {
/* 121 */           this.locked = true;
/* 122 */           return true;
/*     */         }
/*     */       }
/* 125 */       timeout -= 100;
/* 126 */       Thread.sleep(100L);
/*     */     }
/* 128 */     return false;
/*     */   }
/*     */ 
/*     */   public synchronized void release()
/*     */   {
/* 135 */     release(this.jedis);
/*     */   }
/*     */ 
/*     */   public synchronized void release(RedisProxy jedis)
/*     */   {
/* 142 */     if (this.locked) {
/* 143 */       jedis.del(this.lockKey);
/* 144 */       this.locked = false;
/*     */     }
/*     */   }
/*     */ }
本人的疑问点在于: 当前时间2017-12-23 00:00:00:000 如果a线程即将过期未过期(过期时间2017-12-23 00:00:00:100),获取分布式锁, 此时b线程进入,时间为2017-12-23 00:00:00:200并且(a线程未执行完毕),b线程发现锁的时间已经过期,竞争最后获取锁 c线程在等待时,a线程执行完毕,unlock操作删除lockKey,c也获取锁 那么就导致两个线程同时执行同一个的可能性 (有人会说a线程设置的过期时间太短,你可以理解为a线程过期时间很长,但是a线程执行时其他数据锁表了,一直阻塞导致当前时间超过执行时间的可能性)
我做的时候会加入判断
if(System.nanoTime()-begin<workTime) {
                    if (myLock.del(channelType) == 1L) {
                        System.out.println("释放锁成功");
                    } else {
                        System.out.println("释放锁失败");
                    }
                }
其中workTime就是redis设置key时候的生存时间 所以发现超时了,此时肯定key不会有了,如果有也不是自己的,所以不能删除 这样B线程的key就不会被A删掉 你要说从A判断超时时间点到删除操作时间点之间刚好key失效而B又抢占了的话 只能说这种几率十分少 而且目前并没有绝对完美的解决办法
  • 打赏
  • 举报
回复

25,985

社区成员

发帖
与我相关
我的任务
社区描述
高性能WEB开发
社区管理员
  • 高性能WEB开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧