一、鎖未被釋放
問題描述:在高併發情況下,如果執行緒獲取到鎖後,由於異常或其他原因沒有釋放鎖,會導致其他執行緒無法獲取到鎖,從而影響程式的正常執行。
解決方案:確保在
finally
塊中釋放鎖,以保證鎖一定會被釋放。程式碼示例:
public String stockLock() { RLock lock = redissonClient.getLock("stockLock"); try { if (lock.tryLock(10, TimeUnit.SECONDS)) { // 業務邏輯處理 } } finally { lock.unlock(); } return "ok"; }
二、B的鎖被A給釋放了
問題描述:多個執行緒或程序使用相同的鎖值去釋放鎖,可能會導致一個執行緒釋放了另一個執行緒的鎖。
解決方案:使用一個唯一識別符號(如UUID)作為鎖的值,確保只有持有相同識別符號的執行緒才能釋放鎖。
程式碼示例:
String uniqueId = UUID.randomUUID().toString(); RLock lock = redissonClient.getLock("stockLock"); if (lock.tryLock()) { try { // 業務邏輯處理 } finally { if (lock.isHeldByCurrentThread() && lock.getHoldCount() > 0) { lock.unlock(); } } }
三、資料庫事務超時
問題描述:在分散式系統中,資料庫事務可能會因為執行時間過長而超時,導致鎖的釋放和業務邏輯執行不一致。
解決方案:避免在分散式鎖的獲取和釋放過程中使用資料庫事務,或者手動控制事務的提交和回滾。
程式碼示例:
@Autowired DataSourceTransactionManager transactionManager; public void processWithLock() { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { RLock lock = redissonClient.getLock("stockLock"); if (lock.tryLock()) { try { // 執行業務邏輯 } finally { lock.unlock(); } } transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); } }
四、鎖過期了,業務還沒執行完
問題描述:在業務邏輯執行時間較長的情況下,鎖可能會在業務邏輯執行完畢前過期,導致其他執行緒可以獲取到鎖。
解決方案:使用具有自動續期功能的客戶端(如Redisson),它會在鎖即將過期時自動續期。
程式碼示例:
RLock lock = redissonClient.getLock("stockLock"); if (lock.tryLock()) { try { // 執行業務邏輯 // Redisson會自動續期鎖 } finally { lock.unlock(); } }
五、redis主從複製的坑
問題描述:在Redis主從複製模式下,如果主節點宕機,從節點晉升爲主節點,可能會導致多個客戶端同時持有鎖。
解決方案:避免在主從複製模式下使用分散式鎖,或者使用Redis Cluster模式來提高鎖的安全性。
程式碼示例:
// 在Redis Cluster模式下使用分散式鎖 RLock lock = redissonClient.getLock("stockLock"); if (lock.tryLock()) { try { // 執行業務邏輯 } finally { lock.unlock(); } }