加鎖
/*** 嘗試為指定的許可證 ID 獲取分布式鎖。如果鎖已被占用,則立即拋出業務異常。** @param licenseId 需要加鎖的許可證 ID(即鎖名稱)* @return true 表示成功獲取鎖,但請注意:* 鎖實際持有時間為 30 秒(或自動續期),* 調用方在業務完成后必須主動釋放鎖!* @throws BizException 當鎖被占用或發生中斷異常時拋出*/
public Boolean addLockLicenseId(String licenseId) {// 1. 獲取Redisson分布式鎖對象:以licenseId作為鎖的唯一標識RLock lock = redissonClient.getLock(licenseId);try {// 2. 嘗試獲取鎖(核心邏輯)// - 等待時間(0): 不等待,立即返回獲取結果// - 租期時間(30秒): 成功獲取鎖后,鎖自動在30秒后過期釋放// - leaseTime = 0 會啟用看門狗自動續期(慎用,需確保解鎖)boolean locked = lock.tryLock(0, 30, TimeUnit.SECONDS);if (locked) {// 3. 成功獲取鎖:直接返回true給調用方// 關鍵:調用方必須在處理完業務后主動調用lock.unlock()釋放鎖!// 否則30秒后鎖會自動過期,但被動等待過期可能導致資源浪費return true;} else {// 4. 鎖已被占用:拋出自定義業務異常// ERR_OPT_FAIL建議提示:"操作失敗,請稍后重試"throw new BizException(MessageConstant.ERR_OPT_FAIL);}} catch (InterruptedException e) {// 5. 處理中斷異常(重要:恢復線程中斷狀態)// - 避免中斷信號被吞沒導致線程無法響應停止請求// - 對支持可中斷方法的框架(如線程池)非常關鍵Thread.currentThread().interrupt();throw new BizException("獲取鎖時被中斷,請重試!");}// 注意:沒有finally塊釋放鎖!釋放鎖的職責轉交給調用方(成功獲取時)
}
解鎖
/*** 釋放指定許可證ID對應的分布式鎖(僅限當前線程持有的鎖)* * <p>該方法通過Redisson客戶端獲取鎖對象后,校驗當前線程是否持有該鎖,* 避免非法釋放其他線程持有的鎖導致并發安全問題。</p>** @param licenseId 許可證ID(業務唯一標識),用于構造分布式鎖的Redis鍵* * @see RLock#isHeldByCurrentThread() 驗證鎖持有者的線程安全性* @see RLock#unlock() 釋放鎖的核心操作*/
public void unLockLicenseId(String licenseId) {// 構造分布式鎖的Redis鍵,格式:"{licenseId}"// 注意:此鍵必須與加鎖時使用的鍵完全一致,否則無法定位同一把鎖[6](@ref)RLock lock = redissonClient.getLock(licenseId);// 校驗當前線程是否持有該鎖(關鍵防御性檢查)// 1. 防止釋放其他線程持有的鎖(避免IllegalMonitorStateException)// 2. 避免鎖過期自動釋放后的無效解鎖操作[7](@ref)if (lock.isHeldByCurrentThread()) {// 僅當當前線程持有鎖時執行釋放操作// 注意:unlock()會遞減鎖的重入計數器,計數器歸零時完全釋放鎖[6](@ref)lock.unlock();}// 若非當前線程持有鎖,此處靜默跳過(避免拋出異常中斷主流程)
}