同步鎖-Synchronization

thin lock 瘦化鎖=輕量鎖

同步與對象鎖

Java編程語言的一個主要優勢是它內建支持多線程程序。爲了同步訪問可以對一個對象加鎖使其可以被多個線程共享。Java提供了指定臨界代碼區(本文簡稱臨界區)的原語,臨界代碼區作用於共享對象並且在一個時間點只能有一個線程執行。進入臨界區的第一個線程鎖定共享對象。當第二個線程要進入同一個臨界區時,它必須等待直到第一個線程釋放這個共享對象的鎖。
在Java HotSpot VM中,每一個對象前面是一個class指針與一個對象頭。對象頭存儲着唯一指代對象的hash碼與年齡和分代垃圾回收的標誌位,標誌位也用於實現thin lock(瘦化鎖)[Agesen99, Bacon98]。下面的圖展示了對象頭的佈局設計和不同對象狀態的表示。
1
圖的右邊舉例說明了標準鎖的過程。如果一個對象釋放了鎖,那麼最後兩位的值爲01。當方法在對象上進行同步時,對象頭與指向對象的指針被存儲在當前棧幀的lock record中。VM使用CAS操作試圖在對象頭中設置一個指向lock record的指針。如果成功,當前線程將擁有該鎖。因爲lock record總是按字邊界對齊,因此對象頭的最後兩位是00,並且將對象標識爲已鎖定。

如果因爲在這之前對象已被鎖定而導致CAS操作失敗,VM首先比較對象頭的指針是否指向當前線程方法棧。在這種情況下(是指向當前線程方法棧),線程已經擁有對象的鎖並且可以安全的繼續它的執行。對於像上面這樣的遞歸鎖定對象,lock record的初始值是0而不是對象頭。如果兩個不同的線程併發同步在一個對象上,瘦化鎖必須膨脹爲一個重量鎖來管理等待的線程。

瘦化鎖比膨脹鎖成本低很多,但是他們的性能遭受在多處理器機器上每個CAS操作必須原子的執行影響,儘管大多對象只由一個特定的線程鎖定與釋放。在Java6中,這個缺點由一個被稱爲_store-free biased locking technique [Russell06](無存儲偏向鎖技術)_的技術解決,該技術使用類似[Kawachiya02]的概念。僅第一個鎖獲取執行一個原子的CAS操作將鎖定線程的ID設置到對象頭中。然後說這個對象偏向該線程。未來該對象鎖的鎖定與釋放鎖對於同一個線程不需要任何原子的操作或者對象頭的更新操作。甚至棧上的lock record也不需要(離開未初始化狀態)初始化,因爲它將永遠不檢查一個偏向對象。

當一個線程在一個偏向其他線程的對象上執行同步時,必須通過使該對象看起來像是已被常規方式鎖定一樣來消除偏見。遍歷偏向者的棧,按照瘦化鎖方案調整該對象關聯的lock record,並在對象頭中設置一個指向他們中最舊lock record的指針(個人理解是在兩個線程競爭偏向鎖時,發生了競爭,升級爲輕量鎖(或重量鎖),此時輕量級鎖的對象頭指針指向所有lock record中最舊的一個,最早獲取鎖的線程。釋放鎖時會發現偏向鎖被撤銷後會按照輕量鎖(或重量鎖)的方式釋放鎖)。爲了這個操作所有的線程必須暫停。當訪問對象身份hash碼時,由於這個hash碼與線程ID共享對象頭中的bit位,因此也消除了偏見。

明確設計爲多個線程共享對象,像生產者/消費者隊列,不適合使用偏向鎖。因此,一個類如果它的實例在過去頻繁發生撤銷,那麼該類將禁用偏向鎖。這被稱爲_bulk revocation_批量撤銷。如果在被禁用偏向鎖的類實例上調用鎖定代碼,它將執行標準瘦化鎖的鎖定方式。新分配的該類實例被標記爲不可偏向。

一個類似的機制,被稱爲_bulk rebiasing_批量重置偏向,優化類的實例被不同的線程但是從不併發鎖定和釋放鎖的情況。它使類的所有實例的偏向失效,但不禁用偏向鎖。類中的epoch紀元值作爲表示偏向的有效性的一個時間戳。在對象分配時,該值被拷貝到對象頭。在合適的類中批量重置偏向可以由epoch值的增量高效的實現。下次要鎖定此類的實例時,代碼在對象頭中檢測到一個不同值,並重置該對象偏向至當前線程。

源代碼提示

同步Synchronization影響JVM的多個部分:對象頭被定義在 oopDescmarkOopDesc 類結構中,這些代碼爲了瘦化鎖被集成在解釋器與編譯器中,以及表示膨脹鎖的 ObjectMonitor 類。偏向鎖集中在 BiasedLocking 類。偏向鎖可以由參數 -XX:+UseBiasedLocking 指定啓用和 -XX:-UseBiasedLocking 指定禁用。在Java6和Java7中它是默認啓用的,但是在應用啓動幾秒後才被激活。因此,要注意短期運行的微型基準測試micro-benchmarks。如果需要,可以使用參數 -XX:BiasedLockingStartupDelay=0 關閉延遲。

參考資料

[Agesen99] O. Agesen, D. Detlefs, A. Garthwaite, R. Knippel, Y. S. Ramakrishna, D. White: An Efficient Meta-lock for Implementing Ubiquitous Synchronization. In Proceedings of the ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications, pages 207-222. ACM Press, 1999. doi:10.1145/320384.320402
[Bacon98] D. F. Bacon, R. Konuru, C. Murthy, M. Serrano: Thin Locks: Featherweight Synchronization for Java. In Proceedings of the ACM SIGPLAN Conference on Programming Language Design and Implementation, pages 258-268. ACM Press, 1998. doi:10.1145/277650.277734
[Kawachiya02] K. Kawachiya, A. Koseki, T. Onodera: Lock Reservation: Java Locks can Mostly do without Atomic Operations. In Proceedings of the ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications, pages 130-141. ACM Press, 2002. doi:10.1145/582419.582433
[Russel06] K. Russell, D. Detlefs: Eliminating Synchronization-Related Atomic Operations with Biased Locking and Bulk Rebiasing. In Proceedings of the ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications, pages 263-272. ACM Press, 2006. doi:10.1145/1167473.1167496

對象頭

源碼註釋中描述的很清晰,優秀的註釋很重要_
http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/oops/markOop.hpp

Bit-format of an object header (most significant first, big endian layout below):

 32 bits:
 --------
            hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
            JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
            size:32 ------------------------------------------>| (CMS free block)
            PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)

 64 bits:
 --------
 unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
 JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
 PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
 size:64 ----------------------------------------------------->| (CMS free block)

 unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
 JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
 narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
 unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)

- hash contains the identity hash value: largest value is
  31 bits, see os::random().  Also, 64-bit vm's require
  a hash value no bigger than 32 bits because they will not
  properly generate a mask larger than that: see library_call.cpp
  and c1_CodePatterns_sparc.cpp.

- the biased lock pattern is used to bias a lock toward a given
  thread. When this pattern is set in the low three bits, the lock
  is either biased toward a given thread or "anonymously" biased,
  indicating that it is possible for it to be biased. When the
  lock is biased toward a given thread, locking and unlocking can
  be performed by that thread without using atomic operations.
  When a lock's bias is revoked, it reverts back to the normal
  locking scheme described below.

  Note that we are overloading the meaning of the "unlocked" state
  of the header. Because we steal a bit from the age we can
  guarantee that the bias pattern will never be seen for a truly
  unlocked object.

  Note also that the biased state contains the age bits normally
  contained in the object header. Large increases in scavenge
  times were seen when these bits were absent and an arbitrary age
  assigned to all biased objects, because they tended to consume a
  significant fraction of the eden semispaces and were not
  promoted promptly, causing an increase in the amount of copying
  performed. The runtime system aligns all JavaThread* pointers to
  a very large value (currently 128 bytes (32bVM) or 256 bytes (64bVM))
  to make room for the age bits & the epoch bits (used in support of
  biased locking), and for the CMS "freeness" bit in the 64bVM (+COOPs).

  [JavaThread* | epoch | age | 1 | 01]       lock is biased toward given thread
  [0           | epoch | age | 1 | 01]       lock is anonymously biased

- the two lock bits are used to describe three states: locked/unlocked and monitor.

  [ptr             | 00]  locked             ptr points to real header on stack
  [header      | 0 | 01]  unlocked           regular object header
  [ptr             | 10]  monitor            inflated lock (header is wapped out)
  [ptr             | 11]  marked             used by markSweep to mark an object
                                             not valid at any other time

  We assume that stack/thread pointers have the lowest two bits cleared.

原文

https://wiki.openjdk.java.net/display/HotSpot/Synchronization

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