基於redis的分布式鎖 RedissonLock解鎖異常解決


問題現象

在並發操作的場景下(對業務接口連續請求三次),使用基於redis的分布式鎖 RedissonLock解鎖時拋出異常。

問題復現代碼

 

public boolean testLock(Integer type) {
        RLock lock = redissonClient.getLock("testLock" + 22);
        log.info("testLock:1:" + lock.toString() + ",interrupted:" +
                Thread.currentThread().isInterrupted() + ",hold:" +
                lock.isHeldByCurrentThread() + ",threadId:" +
                Thread.currentThread().getId() + ",redissonClient:{}" + redissonClient);

        try {
            boolean res = lock.tryLock(3, 60, TimeUnit.SECONDS);
            log.info("testLock:2:" + lock.toString() + ",interrupted:" +
                    Thread.currentThread().isInterrupted() + ",hold:"
                    + lock.isHeldByCurrentThread() + ",threadId:" + Thread.currentThread().getId()
                    + ",redissonClient:{}" + redissonClient + ",res:{}" + res);
            if (res) {
                // 業務代    
                Thread.sleep(4000L);
            }
        } catch (InterruptedException e) {
            log.error("分布式鎖{}獲取失敗", lock.getName());
            throw new BusinessProcessFailException("分布式鎖【" + lock.getName() + "】獲取失敗", ErrorCode.SYSTEM_EXCEPTION.getCode());
        } finally {
            log.info("testLock:3:" + lock.toString() + ",interrupted:" +
                    Thread.currentThread().isInterrupted() + ",hold:" +
                    lock.isHeldByCurrentThread() + ",threadId:" + Thread.currentThread().getId() + ",redissonClient:{}" + redissonClient);
            lock.unlock();
        }
        return false;
    }

 

具體的異常信息如下

java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 1f24378c-5456-4321-827a-bc0a7515ec5d thread-id: 227
at org.redisson.RedissonLock.unlock(RedissonLock.java:366) ~[redisson-2.10.5.jar:na]

排查過程

業務代碼中添加日志打印

log.info("testLock:1:" + lock.toString()                                   // lock鎖對象地址

+ ",interrupted:" + Thread.currentThread().isInterrupted()        // 當前線程是否中斷

+ ",hold:" + lock.isHeldByCurrentThread()                            // 當前請求的線程是否是鎖對象的持有者

+ ",threadId:" + Thread.currentThread().getId()                     // 線程ID

+ ",redissonClient:{}" + redissonClient)                                // redissonClient地址

+ ",res:{}" + res);                                                              // 線程加鎖結果

日志

  • testLock:1:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:false,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80               
  • testLock:2:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true
  • testLock:1:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80
  • testLock:1:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:false,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80
  • testLock:2:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80,res:{}false
  • testLock:3:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80
  • testLock:3:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80
  • testLock:2:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true
  • testLock:3:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80

通過日志數據可知

線程 242、235、244 使用的是同一個redissonClient

線程 242、235、244 使用的lock鎖對象不是同一個,242是6132b8ee,235是33e44749,244是6da98d58

線程執行流程

testLock:1:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:false,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80                    // 線程242構建鎖對象

testLock:2:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true      // 線程 242 加鎖成功

testLock:1:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80                    // 線程235構建鎖對象

testLock:1:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:false,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80                    // 線程 244 構建鎖對象

testLock:2:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80,res:{}false    // 線程235加鎖失敗

testLock:3:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80                   // 線程235 最終會走到finally 執行解鎖,但是解鎖失敗(該線程並沒有獲取到鎖)

testLock:3:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80                     // 線程 242 解鎖成功

testLock:2:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true      // 線程244加鎖成功

testLock:3:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80                     // 線程244解鎖成功

解決方法

解鎖時添加判斷

if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}

 

lock.isLocked():判斷要解鎖的key是否已被鎖定。

lock.isHeldByCurrentThread():判斷要解鎖的key是否被當前線程持有。

 

 

結論

1、只有加鎖成功才需要解鎖

2、自己加的鎖,自己解,不能解別人的鎖

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM