Java多線程——鎖概念與鎖優化
悲觀鎖與樂觀鎖
- 樂觀鎖。樂觀的想法,認為並發讀多寫少。每次操作的時候都不上鎖,直到更新的時候才通過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替換指向失敗,則說明存在多線程競爭,此時鎖會膨脹為輕量級鎖。
自旋鎖
是鎖競爭失敗後執行的策略之一,對應的有阻塞的鎖。在線程競爭鎖失敗後,阻塞的鎖會把線程阻塞,直到有信號喚起才能繼續執行線程,此過程會涉及用戶態與系統態的轉換,產生性能消耗。而自旋鎖在鎖競爭失敗後,會把線程做自旋,避免線程進入阻塞。在自旋過程中,會不斷的嘗試去競爭鎖。
Java多線程——鎖概念與鎖優化