同一個進程
1.重入鎖(ReentrantLock)
使用ReentrantLock獲取鎖的時候會判斷當前線程是否爲獲取鎖的線程,如果是則將同步的狀態+1,釋放鎖的時候將同步的狀態-1,只有將同步狀態的次數爲0時纔會最終釋放鎖。
重入鎖:一個線程獲得了鎖之後仍然可以反覆加鎖,而不會出現自己阻塞自己的情況。
2.讀寫鎖
使用ReentrantReadWriteLock
,同時維護一對鎖:讀鎖和寫鎖,當寫線程訪問時其他所有鎖都將阻塞,讀線程訪問時則不會。通過將讀寫鎖分離可以很大程度的提高併發量和吞吐量。
不同進程
分佈式鎖:
- 基於 DB 的唯一索引。
可以創建一張表,將其中的某個字段設置爲唯一索引,當多個請求多來的時候只有新建記錄成功的請求才算獲取到鎖,當使用完畢後刪除這條記錄的時候即釋放鎖。
存在的問題:
- 數據庫單點的問題,掛了怎麼辦?
- 不是重入鎖,同一個進程無法在釋放鎖之前再次獲取鎖,因爲數據庫中已經存在了一條記錄了
- 鎖是非阻塞的,一旦
insert
失敗則會立即返回,並不會進入阻塞隊列只能下一次在獲取 - 鎖沒有失效時間,如果那個進程解鎖失敗那就沒有請求可以再次獲取鎖了
解決方案:
- 數據庫切換爲主從,不存在單點。
- 在表中加入一個同步狀態字段,每次獲得鎖的時候加1,釋放鎖的時候減1,當狀態值爲0時就刪除這一條記錄,即釋放鎖。
- 非阻塞的情況可以用
while
循環來實現,循環的時候記錄時間,達到X秒標記爲超時,break; - 可以開啓一個定時任務每個一段時間掃描找出過了X秒都沒有被刪除的記錄,主動刪除這條記錄。
- 基於 ZK 的臨時有序節點。
可以參考:
Zookeeper 分佈式鎖 - 基於 Redis 的 NX EX 參數。
使用setnX(key) setEX(timeout)命令
,只有在該key
不存在的時候纔會創建這個key
,就相當於獲取了鎖。由於有超時時間,所以過了規定時間會自動刪除,這樣也可以避免死鎖。
可以參考:
Redlock(redis分佈式鎖)原理分析