thin lock 瘦化锁=轻量锁
同步与对象锁
Java编程语言的一个主要优势是它内建支持多线程程序。为了同步访问可以对一个对象加锁使其可以被多个线程共享。Java提供了指定临界代码区(本文简称临界区)的原语,临界代码区作用于共享对象并且在一个时间点只能有一个线程执行。进入临界区的第一个线程锁定共享对象。当第二个线程要进入同一个临界区时,它必须等待直到第一个线程释放这个共享对象的锁。
在Java HotSpot VM中,每一个对象前面是一个class指针与一个对象头。对象头存储着唯一指代对象的hash码与年龄和分代垃圾回收的标志位,标志位也用于实现thin lock(瘦化锁)[Agesen99, Bacon98]。下面的图展示了对象头的布局设计和不同对象状态的表示。

图的右边举例说明了标准锁的过程。如果一个对象释放了锁,那么最后两位的值为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的多个部分:对象头被定义在 oopDesc 和 markOopDesc 类结构中,这些代码为了瘦化锁被集成在解释器与编译器中,以及表示膨胀锁的 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
本文深入探讨Java中的锁机制,包括thinlock(瘦化锁)的运作原理,以及如何通过偏向锁技术提升多线程环境下对象锁的效率。介绍了对象头的内部结构,以及在不同锁状态下的变化,对比了轻量级锁与重量级锁的区别。
745

被折叠的 条评论
为什么被折叠?



