鎖
1、分類
樂觀鎖:在select的時候不會加鎖,是基于程序實現的,所以不會存在死鎖的情況。適用于讀多寫少的場景(寫的并發量相對不高),可以提高系統的吞吐量。因為如果寫多的話,樂觀鎖會有很大機率更新失敗,需要不斷的自旋執行查找和更新操作。自旋的時候會一直占用CPU,會耗費大量的CPU資源。悲觀鎖:在select的時候就會加鎖,采用先加鎖后處理的模式,雖然保證了數據處理的安全性,但也會阻塞其他線程的寫操作。悲觀鎖適用于寫多讀少的場景,因為拿不到鎖的線程,會將線程掛起,交出CPU資源,可以把CPU給其他線程使用,提高了CPU的利用率。鎖分類:悲觀鎖:具有強烈的獨占和排他特性,在整個數據處理過程中,將數據處于鎖定狀態。適合于寫比較多,會阻塞讀操作。樂觀鎖:采取了更加寬松的加鎖機制,大多是基于數據版本( Version )及時間戳來實現。。適合于讀比較多,不會阻塞讀獨占鎖:互斥鎖、排他鎖:保證在任一時刻,只能被一個線程獨占排他持有。synchronized、ReentrantLock
共享鎖:可同時被多個線程共享持有。CountDownLatch到計數器、Semaphore信號量 可重入鎖:又名遞歸鎖。同一個線程在外層方法獲取鎖的時候,在進入內層方法時會自動獲取鎖。
不可重入鎖:公平鎖: 有優先級的鎖,先來先得,誰先申請鎖就先獲取到鎖
非公平鎖: 無優先級的鎖,后來者也有機會先獲取到鎖自旋鎖: 當線程嘗試獲取鎖失敗時(鎖已經被其它線程占用了),無限循環重試嘗試獲取鎖
阻塞鎖: 當線程嘗試獲取鎖失敗時,線程進入阻塞狀態,直到接收信號后被喚醒。在競爭激烈情況下,性能較高讀鎖: 共享鎖
寫鎖: 獨占排他鎖偏向鎖:一直被一個線程所訪問,那么該線程會自動獲取鎖
輕量級鎖:(CAS):當鎖是偏向鎖的時候,被另一個線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,提高性能。
重量級鎖:當鎖為輕量級鎖的時候,另一個線程雖然是自旋,但自旋不會一直持續下去,當自旋一定次數的時候(10次),還沒有獲取到鎖,就會進入阻塞,該鎖膨脹為重量級鎖。重量級鎖會讓他申請的線程進入阻塞,性能降低。
以上其實是synchronized的鎖升級過程表級鎖: 對整張表加鎖,加鎖快開銷小,不會出現死鎖,但并發度低,會增加鎖沖突的概率
行級鎖: 是mysql粒度最小的鎖,只針對操作行,可大大減少鎖沖突概率,并發度高,但加鎖慢,開銷大,會出現死鎖
2、具體鎖實現:
1、jvm:
ReentrantLock悲觀的獨占的可重入的可公平可不公平鎖synchronized悲觀的獨占的可重入的非公平鎖無鎖 --> 偏向鎖(同一個線程再次獲取鎖) --> 輕量級鎖(自旋) --> 重量級鎖
2、mysql:
select ... for update:悲觀的獨占的
select ... lock in share mode
?
3、jvm:ReentrantLock + synchronized
1.單個jvm實例 單機2.必須單例3.與事務并存問題總之,不適合于保證數據庫數據可靠性
?
4、mysql:
1.直接更新時判斷。在更新中判斷庫存是否大于0 update table set surplus = (surplus - buyQuantity) where id = 1 and (surplus - buyQuantity) > 0 ;解決jvm鎖多例模式鎖失效問題 及 事務共存問題鎖范圍控制:條件字段必須創建索引;查詢條件必須具體的值同一個商品有多個庫存時,無法解決。無法記錄庫存變化前后的狀態2.悲觀鎖:select ... for update庫存操作要統一:不能有的操作是select ... for update 而有的操作是普通的select死鎖風險:多條記錄時,加鎖順序要一致阻塞及性能問題3.樂觀鎖:version 或者 時間戳(CAS思想)ABA問題失敗需要重試,高并發情況下性能不高讀寫分離情況下導致樂觀鎖不可靠
5、zookeeper
客戶端:ZooKeeper原生客戶端、ZkClient、Curator前兩個客戶端參照:https://blog.csdn.net/qq_42349306/article/details/118209298
讀操作和設置監聽事件之間是有原子性的阻塞公平鎖:1.接收到請求時,在/locks節點下創建一個臨時序列化節點2.判斷自己是不是/locks節點下最下的節點:是則獲取到鎖,不是則監聽前一個節點3.獲取到鎖,處理完業務邏輯后,通過delete刪除當前節點釋放鎖。監聽當前節點的下一個節點收到通知,重復第二步。
Curator分布式鎖源碼解讀:https://blog.csdn.net/qq_41432730/article/details/123389670