Java併發機制的底層實現原理

Java併發編程的藝術筆記

Java代碼 編譯之後 得到 Java字節碼,被 類加載器加載到JVM中,最終 轉化爲彙編指令。

volatile

volatile是輕量級的synchronized,被volatile修飾的變量,在一個線程能讀到這個變量被另一個線程修改之後的值。
volatile不會引起線程上下文切換和調度。

volatile的兩條實現原則

  • Lock前綴指令會引起處理器緩存回寫到內存.
  • 一個處理器的緩存回寫到內存會導致其他處理器的緩存無效.

synchronized實現同步

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

Synchonized在JVM裏的實現原理

JVM 基於進入和退出Monitor對象來實現方法同步和代碼塊同步。

  • 代碼塊同步是使用monitorentermonitorexit指令實現的
  • 方法同步是使用另外一種方式實現的,細節在JVM規範裏並沒有詳細說明。但是,方法的同步同樣可以使用monitorentermonitorexit指令來實現。

monitorenter指令是在編譯後插入到同步代碼塊的開始位置,monitorexit是插入到方法結束處和異常處。
JVM要保證每個monitorenter必須有對應的monitorexit與之配對。
任何對象都有一個monitor與之關聯,當且一個monitor被持有後,它將處於鎖定狀態。線程執行到monitorenter指令時,將會嘗試獲取對象所對應的monitor的所有權,即嘗試獲得對象的鎖。

Java對象頭

synchronized用的鎖是存在Java對象頭裏的。在32位 虛擬機中,1字寬 等於4字節,即32bit

  • 數組類型,虛擬機用3個字寬存儲對象頭。
  • 非數組類型,虛擬機用2個字寬存儲對象頭。

鎖的4種狀態

級別從低到高依次是:

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

Java中的鎖介紹

原子操作的實現原理

原子(atomic)本意是“不能被進一步分割的最小粒子”,而原子操作(atomic operation)意爲“不可被中斷的一個或一系列操作”。

  • 處理器如何實現原子操作

    • 使用總線鎖保證原子性:所謂總線鎖就是使用處理器提供的一個LOCK#信號,當一個處理器在總線上輸出此信號時,其他處理器的請求將被阻塞住,那麼該處理器可以獨佔共享內存。
  • 使用緩存鎖保證原子性
    以下兩種情況不會使用緩存鎖:

    • 當處理器不支持緩存鎖定。
    • 當操作的數據不能被緩存在處理器內部,或操作的數據跨多個緩存行時,則處理器會調用總線鎖定。
  • Java如何實現原子操作

  • CAS實現原子操作的三大問題:

    • ABA問題:因爲CAS需要在操作值的時候,檢查值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。
      ABA問題的解決思路就是 使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加1,那麼A→B→A就會變成1A→2B→3A
      原子操作類AtomicStampedReferencecompareAndSet方法的作用是首先檢查當前引用是否等於預期引用,並且檢查當前標誌是否等於預期標誌,如果全部相等,則以原子方式將該引用和該標誌的值設置爲給定的更新值。
    • 循環時間長開銷大:自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷。
    • 只能保證一個共享變量的原子操作:當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖。還有一個取巧的辦法,就是把多個共享變量合併成一個共享變量來操作。
  • 使用鎖機制實現原子操作

    • 鎖機制保證了只有獲得鎖的線程才能夠操作鎖定的內存區域。

如果覺得不錯的話,請幫忙點個讚唄。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章