1.原理: 當同一個用戶獲取鎖以後,會讓該用戶一直持有鎖。一樣 的用戶再次獲取,會根據原子性 ,lock返回true。redis
/** * 獲取鎖(非公平鎖), 默認獲取超時爲2分鐘 */ public boolean lock(){ return lock(GETLOCK_TIMEOUT/1000); } /** * 獲取鎖(非公平鎖), 獲取超時爲timeoutSeconds秒 */ public boolean lock(int timeoutSeconds){ int timeout = timeoutSeconds*1000; while (timeout >= 0) { //在同一個JVM內,防止出現如下狀況:當已經獲取此鎖的線程實際業務運行時間超過LOCK_EXPIRE_TIMEOUT時,此鎖會再被其它線程獲取 if(LOCKED_NAMES.contains(lockname)){ int sleeptime = random.nextInt(100)+100; try{Thread.sleep(sleeptime);} catch (InterruptedException e) {} timeout -= sleeptime; continue; } //當分佈式程序獲取同一個鎖時,防止出現如下狀況:當已經獲取此鎖的進程(物理機器)實際業務運行時間超過LOCK_EXPIRE_TIMEOUT時,此鎖會再被其它進程(物理機器)獲取 String hearbeatFlag = RedisCacheUtil.get(lockname + HEARTBEAT_SUFFIX , String.class ); if(hearbeatFlag!=null){ int sleeptime = random.nextInt(100)+100; try{Thread.sleep(sleeptime);} catch (InterruptedException e) {} timeout -= sleeptime; continue; } //鎖到期時間 long expires = System.currentTimeMillis() + LOCK_EXPIRE_TIMEOUT + 1; boolean ok = setNX(expires); if (ok) { //System.out.println(Thread.currentThread().getName()+":得到鎖成功,經過setNX"); LOCKED_NAMES.add(lockname); locked = true; return locked; } //獲取當前鎖信息 RedisLockValue redisLockValue = get(); if (redisLockValue != null && redisLockValue.getExpireTime() < System.currentTimeMillis()) { //利用getSet的原子性操做來設置並獲取到舊值 RedisLockValue oldRedisLockValue = getSet(expires); //最早設置的獲取鎖 if (oldRedisLockValue != null && oldRedisLockValue.getToken().equals(redisLockValue.getToken())) { //System.out.println(Thread.currentThread().getName()+":得到鎖成功,經過getSet"); LOCKED_NAMES.add(lockname); locked = true; return locked; } } //防止飢餓線程出現 採用隨機休眠時間 int sleeptime = random.nextInt(100)+100; try{Thread.sleep(sleeptime);} catch (InterruptedException e) {} timeout -= sleeptime; } //若是已經超時,但只是由於此時緩存還有值,由於反序列化異常致使GET取不到時,解決死鎖問題 try{Thread.sleep(300);} catch (InterruptedException e) {} RedisLockValue redisLockValue = get(); if(redisLockValue==null){ //強制刪掉便可 LOCKED_NAMES.remove(lockname); RedisCacheUtil.delete(lockname); RedisCacheUtil.delete( lockname + HEARTBEAT_SUFFIX ); } return locked; }