简介
synchronize 关键字用起来非常简单,但JDK1.6的底层实现却很复杂。
JDK1.6开始JVM对synchronize底层做了优化。
上一篇
https://www.jianshu.com/p/aeb0a68709cb
源代码及部分注释:
https://github.com/sparrowzoo/open-jdk8/blob/master/hotspot/src/share/vm/oops/markOop.hpp
Bit-format of an object header
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)//MaxTenuringThreshold (GC次数)最大为15
// 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)
//
// 64 bits COOPS:
// --------
// 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 (25/31)
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.
hash 是唯一的hash value,最大位是31bits,参见os::random().64位虚拟机也不可能生成大于32位的hash value.
hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits
// Constants
enum { age_bits = 4,
lock_bits = 2,
biased_lock_bits = 1,
max_hash_bits = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits,(hash位数)
cms_bits = LP64_ONLY(1) NOT_LP64(0),
epoch_bits = 2
};
- age(4)
4位,因此MaxTenuringThreshold (GC次数)最大为15
- biased_lock(1)
1位,因为锁标记的两位(01)代码无锁和偏向锁,所以加一位来区分
- lock(2)
2 位,具体见下表 - JavaThread指针
The runtime system aligns all JavaThread* pointers to
a very large value (currently 128 bytes (32bVM) (7) or 256 (8) 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*
以一个非常大的值对齐(128 bytes (32b vm) or 256 (64v VM),为了age bits
epoch bits
以及压缩指针的CMS freeness 位腾出空间。
- epoch(2)
该字段为两位,在锁在起到的作用参见
https://www.zhihu.com/question/56582060/answer/155398235
简单解析:
对象每次初始化时(或者说被加偏向锁时)会从类的头信息中获取该值,每加一次偏向锁类的头信息的值+1,同时对象的值也加+1,当类的值超过4(2bits)时,那么该类将不适合偏向锁。
markOop incr_bias_epoch() {
return set_bias_epoch((1 + bias_epoch()) & epoch_mask);
}
轻量锁和重量锁指针
We assume that stack/thread pointers have the lowest two bits cleared.
让出锁标志两位,所以该指为wordsize-2,因为 java 对象8字节对齐(3位),让出两位没有问题。CMS
其他字段为 cms gc相关。
4个锁级别
- 无锁
[header |0 | 01] unlocked regular object header
[hashCode|age|0|01
- 偏向锁
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.
the biased lock pattern
通常用于偏向指定的线程。当这个模式的低三位被set时,那么这个编向锁可以指向指定的线程或 "anonymously" biased
,来标志这个对象可能被偏向锁了,当这个锁被偏向提定的线程时,加锁和解锁都不需要原子操作
,当锁的偏向解除时,它恢复回原来的schema。
注:Synchronized 会保证可见性
[JavaThread* | epoch | age | 1 | 01] lock is biased toward given thread
[0 | epoch | age | 1 | 01] lock is anonymously biased
- 轻量级锁
[ptr | 00] locked ptr points to real header on stack
这时的指针指向stack 的对象头
参考
https://pdfs.semanticscholar.org/bc8f/7a35b87b452924e180ed15b58f049bcac9db.pdf
- 加锁
- 为线程分配lock record
- copy
object mark word
到lock record
- 通过CAS,尝试将
object mark word
的指针指向lock record,并将lock record owner
的指针指向object mark word
- 解锁
简单理解就是加锁的相反操作,将object mark word
的值从lock record 中还原。
Fast unlock atomically CASs displaced mark word
back into object header
> If success, no contention occurred
> If failed, monitor was inflated into heavyweight case
using OS-level locking primitives
> Enter run-time system, notify waiting threads`
- 重量级锁
[ptr | 10] monitor inflated lock (header is wapped out)
指针指向monitor对象
在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)
// initialize the monitor, exception the semaphore, all other fields
// are simple integers or pointers
ObjectMonitor() {
_header = NULL; //displaced object header word - mark
_count = 0;
_waiters = 0, // number of waiting threads
_recursions = 0;
_object = NULL; //backward object pointer - strong root
_owner = NULL; //pointer to owning thread OR BasicLock
_WaitSet = NULL; //LL of threads wait()ing on the monitor
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; // number of waiting threads
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
由此可知
- 为什么Object.wait 和notify方法需要和Synchronized关键字配合使用。因为Object.wait将线程阻塞至Wait Set 对象,前提是已经拿到锁。
- Synchronized是一个非公平锁。
GC标志(不算锁级别)
[ptr | 11] marked used by markSweep to mark an object
not valid at any other time
CMS GC相关内容待整理
参考资料
http://ifeve.com/java-synchronized/
https://pdfs.semanticscholar.org/bc8f/7a35b87b452924e180ed15b58f049bcac9db.pdf
https://www.jianshu.com/p/fd780ef7a2e8