java併發機制與底層實現原理

java併發機制與底層實現原理

volatile

volatile是輕量級的synchronize,它在多處理器開發中保證了共享變量的「可見性」,由於它不會引發線程上下文的切換和調度,因此比synchronize的使用和執行成本更底。
爲了提升處理速度,處理器不直接和內存進行通訊,而是先將系統內存的數據讀到內部緩存(L1,L2或其餘)後再進行操做,但操做完不知道什麼時候會寫到內存。使用volatile變量,在操做後,JVM會發出lock指令java

  • 將當前處理器緩存行的數據寫回到系統內存
  • 這個寫回內存的操做會使在其餘cpu裏緩存了該內存地址的數據無效

synchronize

同步基礎

synchronize實現同步的基礎,具體表現爲三種形式數組

  • 對於普通同步方法,鎖是當前實例對象
  • 對於靜態同步方法,鎖是當前類的class對象
  • 對於同步方法塊,鎖是Synchronize括號裏配置的對象

當一個線程試圖訪問同步代碼塊時,它首先必須獲得鎖,退出或拋出異常時必須釋放鎖。那麼鎖到底存在那裏,鎖裏會存儲什麼信息。緩存

java對象頭

synchonize用的鎖是存在java對象頭裏的。若是對象是數組類型,則JVM用三個字寬存儲對象頭,若是對象爲非數組類型,則用二個字寬存儲對象頭。32位中,一字寬等於四字節(32bit)多線程

長度 內容 說明
32/64bit Mark Word 存儲對象的hashCode或鎖信息等。
32/64bit Class Metadata Address 存儲到對象類型數據的指針
32/64bit Array length 數組的長度(若是當前對象是數組)

在運行期間Mark Word裏存存儲的數據會隨着鎖標誌位的變化而變化。會成爲下面的一種
圖片描述併發

鎖類型

爲了減小得到鎖與釋放鎖所帶來的性能消耗,引入「偏向鎖」和「輕量級鎖'.因此在java中存在四種狀態性能

  • 無鎖狀態
  • 偏向鎖狀態
  • 輕量級鎖狀態
  • 自旋鎖
  • 重量級鎖狀態

它會隨着競爭狀況逐漸升級。鎖能夠升級但不能降級,意味着偏向鎖升級成輕量級鎖後不能降級成偏向鎖優化

偏向鎖

Hotspot的做者通過以往的研究發現大多數狀況下鎖不只不存在多線程競爭,並且老是由同一線程屢次得到,爲了讓線程得到鎖的代價更低而引入了偏向鎖。當一個線程訪問同步塊並獲取鎖時,會在對象頭和棧幀中的鎖記錄裏存儲鎖偏向的線程ID,之後該線程在進入和退出同步塊時不須要花費CAS操做來加鎖和解鎖。spa

流程圖中展現偏向鎖的獲取釋放以及升級至輕量鎖
圖片描述操作系統

輕量級鎖

1.輕量級鎖加鎖:

線程在執行同步塊以前,JVM會先在當前線程的棧楨中建立用於存儲鎖記錄的空間,並將對象頭中的Mark Word複製到鎖記錄中,官方稱爲Displaced Mark Word。而後線程嘗試使用CAS將對象頭中的Mark Word替換爲指向鎖記錄的指針。若是成功,當前線程得到鎖,若是失敗,表示其餘線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。線程

2.輕量級鎖解鎖

輕量級解鎖時,會使用原子的CAS操做來將Displaced Mark Word替換回到對象頭,若是成功,則表示沒有競爭發生。若是失敗,表示當前鎖存在競爭,鎖就會膨脹成重量級鎖。下圖是兩個線程同時爭奪鎖,致使鎖膨脹的流程圖。
借用網上流程圖以下:
圖片描述

自旋鎖

當竟爭存在時,若是線程能夠很快得到鎖,那麼能夠不在OS層掛起線程(線程切換平均消耗8K個時鐘週期),讓線程多作幾個空操做(自旋)

  1. 若是同步塊過長,自旋失敗,會下降系統性能
  2. 若是同步塊很短,自旋成功,節省線程掛起切換時間,擔升系統性能

鎖對比

優勢 缺點 適用場景
偏向鎖 加鎖和解鎖不須要額外的消耗,和執行非同步方法比僅存在納秒級的差距。 若是線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗。 適用於只有一個線程訪問同步塊場景。</td
輕量級鎖 競爭的線程不會阻塞,提升了程序的響應速度。 若是始終得不到鎖競爭的線程使用自旋會消耗CPU 追求響應時間。同步塊執行速度很是快。
重量級鎖 線程競爭不使用自旋,不會消耗CPU。 線程阻塞,響應時間緩慢。 追求吞吐量。同步塊執行速度較長。

總結

  • 偏向鎖,輕量級鎖,自旋鎖不是JAVA語言層上的優化方法
  • 內置於JVM中的獲取鎖的優化方法與獲取鎖的步驟

    1. 偏向鎖可用可先嚐試偏向鎖
    2. 輕量級鎖可用可先嚐試輕量級鎖
    3. 1與2都失敗,則嘗試自旋鎖
    4. 再失敗,嘗試普通鎖,使用OS互斥量在操做系統層掛起
相關文章
相關標籤/搜索