Java Monitor(管程)

操做系統在面對線程間同步的時候,會支持例如semaphore信號量和mutex互斥量等同步原語,而monitor是在編程語言中被實現的,下面介紹一下java中monitor(監視器/管程:管理共享變量以及對其的操做過程,讓他們支持併發)的實現原理:java

clipboard.png

以一個阻塞隊列的實現來舉例:
clipboard.png編程

同時,java內置的synchronized關鍵字能夠認爲是MESA模型的簡化版,其只能有一個條件變量,但編譯器會自動添加加鎖與解鎖的代碼。synchronized關鍵字能夠修飾實例方法、類方法以及代碼塊,若是修飾的是代碼塊,須要制定關聯的Object;若是修飾的是實例方法,那麼其關聯的對象其實是this;若是修飾的是類方法,那麼其關聯的對象是this.class。這些關聯的對象就是MESA模型裏的條件變量。數組

synchronized實現原理

JVM基於進入和退出monitor對象來實現同步,同步代碼塊採用添加moniterenter、moniterexit,同步方法使用ACC_SYNCHRONIZED標記符隱式實現。每一個對象都有一個monitor與之關聯,運行到moniterenter時嘗試獲取對應monitor的全部權,獲取成功就將monitor的進入數加1(因此是可重入鎖,也被稱爲重量級鎖),不然就阻塞,擁有monitor的線程運行到moniterexit時進入數減1,爲0時釋放monitor。
java中每一個對象都有一個對象頭,synchronized所用的鎖就是存在對象頭裏的。若是是非數組的對象是8個字節(32位JVM)或者16字節(64位JVM),數組對象還會有一個數組長度(4個字節)。以32位JVM非數組對象爲例:緩存

clipboard.png

鎖信息就存在前4個字節的MarkWord中,JVM對synchronized的加鎖過程優化爲:多線程

  1. 檢測Mark Word裏面是否是當前線程的ID,若是是,表示當前線程處於偏向鎖
  2. 若是不是,則使用CAS將當前線程的ID替換Mard Word,若是成功則表示當前線程得到偏向鎖,置偏向標誌位1
  3. 若是失敗,則說明發生競爭,撤銷偏向鎖,進而升級爲輕量級鎖。
  4. 當前線程使用CAS將對象頭的Mark Word替換爲鎖記錄指針,若是成功,當前線程得到鎖
  5. 若是失敗,表示其餘線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。
  6. 若是自旋成功則依然處於輕量級狀態。
  7. 若是自旋失敗,則升級爲重量級鎖。

重量級鎖是悲觀鎖的一種,自旋鎖、輕量級鎖與偏向鎖屬於樂觀鎖。
CAS設計讀取-比較-寫入三個操做,是在CPU指令層面保證其原子性,volatile是保證多線程下的內存可見性,兩者需配合使用。另外還需注意CPU緩存行(一次以32/64字節爲單位從主內存中讀取數據到緩存)包含多個變量所帶來的隱形同步問題:其中一個變量被volatile修飾,致使另一個變量在另外一個CPU核上(另外一個線程)的讀寫也要被強制刷新緩存。併發

管程:併發編程的萬能鑰匙
java 中的鎖 -- 偏向鎖、輕量級鎖、自旋鎖、重量級鎖
相關文章
相關標籤/搜索