Java多執行緒——鎖概念與鎖優化
為了效能與使用的場景,Java實現鎖的方式有非常多。而關於鎖主要的實現包含synchronized關鍵字、AQS框架下的鎖,其中的實現都離不開以下的策略。
悲觀鎖與樂觀鎖
- 樂觀鎖。樂觀的想法,認為併發讀多寫少。每次操作的時候都不上鎖,直到更新的時候才通過CAS判斷更新。對於AQS框架下的鎖,初始就是樂觀鎖,若CAS失敗則轉化為悲觀鎖。
- 悲觀鎖。悲觀的想法,認為併發寫多讀少。每次操作資料都上鎖,即使別人想讀也要先獲得鎖才能讀。對於1.6以前的synchronized關鍵字,則是悲觀鎖的實現之一。
CAS無鎖演算法
全稱為 Compare and Swap。CAS有三個運算元,記憶體值V,舊預期值(已獲得的舊資料)A,修改新值B。當且僅當V與A的值相同(compare),才能把V替換為B(Swap)。其中Java中記憶體值可以通過volatile關鍵字
CAS演算法在鎖的應用非常廣泛,java中concurrent包的高效能都是基於這個演算法,可以說沒有CAS,併發包的高效能也就不存在了。
重量級鎖
悲觀鎖的一種。互斥使程式碼執行可以同步,但這種方式成本比較高,涉及到作業系統的呼叫阻塞,會造成一些系統資源的浪費。1.6以前,在Java中的即是監視器鎖,把.java檔案程式設計成.class檔案後能看到synchronized關鍵字就是通過monitorenter和monitorexit這個兩個位元組碼指令來實現的。
輕量級鎖
由於在沒有很多執行緒競爭的前提下,重量級鎖會導致效能資源的浪費。每次判斷是否無鎖,無鎖則建鎖記錄,有鎖通過CAS去嘗試獲取鎖(對比Mark Word)。該過程失敗會讓鎖膨脹為重量級鎖。
偏向鎖
是輕量級鎖的優化,適用於無多執行緒競爭。雖然輕量級鎖在可以在較少執行緒競爭下,減少作業系統呼叫,減少互斥變數的產生。但在理想情況下,執行緒很少發生執行緒競爭,在輕量級鎖中,還是會有比較多的CAS操作。在偏向鎖中,有一個鎖記錄(Mark word)標記為偏向,指向當前執行緒。若該指向不變,則只需要判斷記錄是否有被切換。如果被切換了,嘗試CAS替換指向,後續一直執行同步程式碼塊。當CAS替換指向失敗,則說明存在多執行緒競爭,此時鎖會膨脹為輕量級鎖。
自旋鎖
是鎖競爭失敗後執行的策略之一,對應的有阻塞的鎖。線上程競爭鎖失敗後,阻塞的鎖會把執行緒阻塞,直到有訊號喚起才能繼續執行執行緒,此過程會涉及使用者態與系統態的轉換,產生效能消耗。而自旋鎖在鎖競爭失敗後,會把執行緒做自旋,避免執行緒進入阻塞。在自旋過程中,會不斷的嘗試去競爭鎖。
但如果執行緒一直自旋都獲取不到鎖,也會產生很多CPU的效能消耗,所以也有一個自適應的自旋鎖
更多技術文章、精彩乾貨,請關注
部落格:zackku.com
微信公眾號:Zack說碼
[](http://qiniu.zackku.com/%E6%89%AB%E7%A0%81_%E6%90%9C%E7%B4%A2%E8%81%94%E5%90%88%E4%BC%A0%E6%92%AD%E6%A0%B7%E5%BC%8F-%E5%BE%AE%E4%BF%A1%E6%A0%87%E5%87%86%E7%BB%BF%E7%89%88.png