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的性能消耗,所以也有一個自適應的自旋鎖(控制自旋的時間)解決這個問題。

發佈了18 篇原創文章 · 獲贊 0 · 訪問量 1562
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章