一、先理理所有情况
假设偏向锁有效,即类没有关闭偏向模式,且其 epoch 没有过期。
则一定会发生偏向锁的撤销。
第一类情况:线程 B 没有正在持有该偏向锁。
- 仅发生单个偏向撤销,偏向锁先被撤销为无锁,然后在 slow_enter 里升级为轻量级锁。
- 触发了批量重偏向,偏向锁先被撤销为匿名偏向,随后立即重偏向于 A。
- 触发了批量撤销,偏向锁同样先被撤销为无锁,然后在 slow_enter 里升级为轻量级锁。
第二类情况:线程 B 正在持有该偏向锁。
- 仅发生单个偏向撤销,偏向锁撤销为 B 的轻量级锁,然后在 slow_enter 里膨胀为重量级锁。
- 触发了批量重偏向,偏向锁撤销为 B 的轻量级锁,然后在 slow_enter 里膨胀为重量级锁。
- 触发了批量撤销,偏向锁撤销为 B 的轻量级锁,然后在 slow_enter 里膨胀为重量级锁。
情况貌似不止这 6 种,因为可能在撤销时,B 正在持有该偏向锁,但是当撤销成功后(撤销过程由 VM 线程在安全点执行,此时 B 被阻塞),它可能立即退出了同步代码块,并把锁从轻量级锁还原成了无锁,此时 A 可能是加轻量级锁成功,也可能是加重量级锁成功,取决于 A 接下来的加锁过程中,B 释放锁的时机。
还有第一类情况中,撤销为无锁,和之后的加轻量级锁也是分离的,可能会加锁失败,然后膨胀为重量级锁。都有可能。
上面所有情况都可以在源码里找到对应的处理逻辑。但后面我只会点出前面 6 种对应源码的处理,其余情况都可以类似分析可得,不再赘述。
批量重偏向和批量撤销的触发条件,可以参考:源码解析-触发批量撤销或批量重偏向的条件
二、从前到后走一遍源码
HotSpot 有 字节码解释器(bytecodeInterpreter) 和 模板解释器(templateInterpreter),前者是 C++ 实现,后者是汇编实现。
HotSpot 实际使用的是 模板解释器,没有用 字节码解释器。但两者逻辑类似,下面从可读性上考虑,给出 字节码解释器 montorenter 指令的解析源码(说的我好像会汇编一样,emm):
源码位置:bytecodeInterpreter.cpp#1816
CASE(_monitorenter): {
oop lockee = STACK_OBJECT(-1);
// derefing's lockee ought to provoke implicit null check
CHECK_NULL(lockee); // lockee 就是锁对象
// find a free monitor or one already allocated for this object
// if we find a matching object then we need a new monitor
// since this is recursive enter
BasicObjectLock* limit = istate->monitor_base();
BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
BasicObjectLock* entry = NULL;
// 找空闲的锁记录
while (most_recent != limit ) {
if (most_recent->obj() == NULL) entry = most_recent;
else if (most_recent->obj() == lockee) break;
most_recent++;
}
if (entry != NULL) {
entry->set_obj(lockee);
int success = false; // 标志变量
uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
markOop mark = lockee->mark();
intptr_t hash = (intptr_t) markOopDesc::no_hash;
// implies UseBiasedLocking
if (mark->has_bias_pattern()) { // 可偏向的情况
uintptr_t thread_ident;
uintptr_t anticipated_bias_locking_value;
thread_ident = (uintptr_t)istate->thread();
anticipated_bias_locking_value =
(((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
~((uintptr_t) markOopDesc::age_mask_in_place);
if (anticipated_bias_locking_value == 0) {
// already biased towards this thread, nothing to do
// case 1:已经偏向于自身,什么都不做。。。。。。。。。。。。。。。。。。
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::biased_lock_entry_count_addr())++;
}
success = true;
}
else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
// try revoke bias
// case 2:试图撤销偏向(批量撤销的漏网之鱼)。。。。。。。。。。。。。。。
// 只有这种情况,success 没有赋为 true
// 因为这种情况是 类元数据(klass)禁用了可偏向属性,即该类发生了批量撤销
// 对于这种情况,放到后面加轻量级锁
markOop header = lockee->klass()->prototype_header();
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics)
(*BiasedLocking::revoked_lock_entry_count_addr())++;
}
}
else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
// try rebias
// case 3:尝试重偏向。。。。。。。。。。。。。。。。。。。。。。。。。。
// 这种情况是因为,对象的 epoch 和 klass 的 epoch 不等
markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
if (hash != markOopDesc::no_hash) {
new_header = new_header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::rebiased_lock_entry_count_addr())++;
}
else {
// 重偏向失败,说明有另一个线程重偏向成功,需要撤销偏向,走下面这个调用
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
else {
// try to bias towards thread in case object is anonymously biased
// case 4:把锁状态当成是匿名偏向处理,尝试偏向于自身。。。。。。。。。。。。
markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
(uintptr_t)markOopDesc::age_mask_in_place |
epoch_mask_in_place));
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
// debugging hint
DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
}
else {
// 尝试失败,两种可能
// 一是:本来是匿名偏向,被别的线程偏向成功,自己失败了,需要撤销偏向
// 二是:本来就不是匿名偏向,而是已经偏向于别的线程,还是需要撤销偏向
// 撤销偏向走下面这个调用
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
}
// traditional lightweight locking
if (!success) { // 走到这不管三七二十一,先加轻量级锁
markOop displaced = lockee->mark()->set_unlocked();
entry->lock()->set_displaced_header(displaced);
bool call_vm = UseHeavyMonitors;
if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
entry->lock()->set_displaced_header(NULL); // 处理轻量级锁重入的情况
} else {
// 加轻量级锁失败,原因有很多
// 可能是,无锁加轻量级锁时,被别的线程先加成功
// 或者,本来就是别人持有的轻量级锁
// 或者,是重量级锁
// 还是走下面这个调用处理
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
}
}
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
} else { // 锁记录不够,新建一个锁记录后,重新执行
istate->set_msg(more_monitors);
UPDATE_PC_AND_RETURN(0); // Re-execute
}
}
线程请求偏向于另一个线程的偏向锁,对应 case 4。
会走到这个逻辑:InterpreterRuntime::monitorenter
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
...
Handle h_obj(thread, elem->obj());
...
if (UseBiasedLocking) { // UseBiasedLocking 参数是默认开启的
// 一般走这里
ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}
...
接着走到 ObjectSynchronizer::fast_enter
// 从 InterpreterRuntime::monitorenter,传入第三个参数 attempt_rebias 为 true
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
if (UseBiasedLocking) {
if (!SafepointSynchronize::is_at_safepoint()) {
// 执行 fast_enter 时不在安全点,一般走这里
BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
return; // 第一类情况里的第二种会在这里直接返回,不会走到后面的 slow_enter
}
} else {
// 执行 fast_enter 时在安全点
...
BiasedLocking::revoke_at_safepoint(obj);
}
...
}
slow_enter (obj, lock, THREAD) ;
}
接下来,先是走到 BiasedLocking::revoke_and_rebias
// 此时,第二个参数 attempt_rebias 为 true
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");
markOop mark = obj->mark(); // 对象 mark word
if (mark->is_biased_anonymously() && !attempt_rebias) {
// 撤销匿名偏向,当需要计算对象的 identity hash code 时,会走到这里
markOop biased_value = mark;
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED;
}
} else if (mark->has_bias_pattern()) {
Klass* k = obj->klass(); // 对象的类元数据
markOop prototype_header = k->prototype_header();
// 类禁用了偏向模式
if (!prototype_header->has_bias_pattern()) {
markOop biased_value = mark;
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);
assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked");
return BIAS_REVOKED;
// 对象的 epoch 不等于 类的 epoch
} else if (prototype_header->bias_epoch() != mark->bias_epoch()) {
if (attempt_rebias) {
assert(THREAD->is_Java_thread(), "");
markOop biased_value = mark;
markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED_AND_REBIASED;
}
} else {
markOop biased_value = mark;
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED;
}
}
}
}
// 下面准备开始偏向撤销,首先判断是否需要批量重偏向或批量撤销
HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
// 对象不是偏向模式,直接返回
if (heuristics == HR_NOT_BIASED) {
return NOT_BIASED;
// 单个偏向的撤销
} else if (heuristics == HR_SINGLE_REVOKE) {
Klass *k = obj->klass();
markOop prototype_header = k->prototype_header();
// 偏向所有者是自身,且 epoch 没有过期
if (mark->biased_locker() == THREAD &&
prototype_header->bias_epoch() == mark->bias_epoch()) {
// 这个分支用于处理,有效地偏向所有者要计算对象 identity hash code 的情况
// 单独处理是因为,这种情况在线程自己的栈处理就好了,不需要等到 safe point
ResourceMark rm;
if (TraceBiasedLocking) {
tty->print_cr("Revoking bias by walking my own stack:");
}
BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD);
((JavaThread*) THREAD)->set_cached_monitor_info(NULL);
assert(cond == BIAS_REVOKED, "why not?");
return cond;
} else {
// 否则,让 VM 线程在 safepoint 调用 VM_RevokeBias 的 doit 方法撤销偏向
VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
VMThread::execute(&revoke);
return revoke.status_code();
}
}
assert((heuristics == HR_BULK_REVOKE) ||
(heuristics == HR_BULK_REBIAS), "?");
// 如果走到这里,说明触发了批量重偏向或批量撤销
// VM 线程在 safepoint 调用 VM_BulkRevokeBias 的 doit 方法
VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD,
(heuristics == HR_BULK_REBIAS),
attempt_rebias); // 此时,这个参数为 true
VMThread::execute(&bulk_revoke);
return bulk_revoke.status_code();
}
看一下 VM_RevokeBias 的 doit 方法(可以将 VM 线程的 doit 方法类比于 Java 线程的 run 方法)
virtual void doit() {
if (_obj != NULL) {
if (TraceBiasedLocking) {
tty->print_cr("Revoking bias with potentially per-thread safepoint:");
}
// 锁对象不为 NULL,调用 revoke_bias 撤销偏向
_status_code = revoke_bias((*_obj)(), false, false, _requesting_thread);
clean_up_cached_monitor_info();
return;
} else {
if (TraceBiasedLocking) {
tty->print_cr("Revoking bias with global safepoint:");
}
BiasedLocking::revoke_at_safepoint(_objs);
}
}
单个对象的偏向撤销,可以参考:源码解析-偏向锁撤销流程解读
撤销结果是:
如果偏向所有者,也就是线程 B,它正在持有该偏向锁,则将该偏向锁撤销为 B 持有的轻量级锁。
如果线程 B 没有正在持有该偏向锁,则将该偏向锁撤销为无锁。
这两种情况都会在随后的 slow_enter 方法进行锁升级。(前面 ObjectSynchronizer::fast_enter 方法里的逻辑)
再看看 VM_BulkRevokeBias 的 doit 方法
virtual void doit() {
_status_code = bulk_revoke_or_rebias_at_safepoint((*_obj)(), _bulk_rebias, _attempt_rebias_of_object, _requesting_thread);
clean_up_cached_monitor_info();
}
如果发生了批量重偏向或批量撤销,VM 线程会执行到 BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint
static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
bool bulk_rebias,
bool attempt_rebias_of_object,
JavaThread* requesting_thread) {
...
// 当前时间
jlong cur_time = os::javaTimeMillis();
// 将这次重偏向时间写入类元数据,作为下次触发批量重偏向或批量撤销的启发条件之一
o->klass()->set_last_biased_lock_bulk_revocation_time(cur_time);
Klass* k_o = o->klass(); // 触发重偏向对象的 类元数据
Klass* klass = k_o;
if (bulk_rebias) {
// 这个分支是批量重偏向的逻辑
// 类开启了偏向模式,才进行批量重偏向
if (klass->prototype_header()->has_bias_pattern()) {
int prev_epoch = klass->prototype_header()->bias_epoch();
// 自增类的 epoch
klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch());
// 获取类自增后的 epoch
int cur_epoch = klass->prototype_header()->bias_epoch();
// 遍历所有线程
for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
// 遍历线程所有的锁记录
for (int i = 0; i < cached_monitor_info->length(); i++) {
MonitorInfo* mon_info = cached_monitor_info->at(i);
oop owner = mon_info->owner();
markOop mark = owner->mark();
// 找到所有当前类的偏向锁对象
if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
// We might have encountered this object already in the case of recursive locking
assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
// 更新该类偏向锁对象的 epoch 与 类的 epoch 保持一致
owner->set_mark(mark->set_bias_epoch(cur_epoch));
}
}
}
}
// 撤销当前偏向锁
// 此时,由于参数 attempt_rebias_of_object 为 true
// 所以,如果偏向所有者没有正在持有该偏向锁,该锁会撤销为匿名偏向锁
// 否则,撤销为轻量级锁
revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread);
} else {
...
// 这个分支是批量撤销的逻辑
// 首先,禁用 类元数据 里的可偏向属性
// markOopDesc::prototype() 返回的是一个关闭偏向模式的 prototype
klass->set_prototype_header(markOopDesc::prototype());
// 其次,遍历所有线程的栈,撤销该类正在被持有的偏向锁为轻量级锁
for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
for (int i = 0; i < cached_monitor_info->length(); i++) {
MonitorInfo* mon_info = cached_monitor_info->at(i);
oop owner = mon_info->owner();
markOop mark = owner->mark();
if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
// 具体撤销,还是通过 revoke_bias() 方法去做
revoke_bias(owner, false, true, requesting_thread);
}
}
}
// 当前锁对象可能未被任何线程持有
// 所以这里单独进行撤销,以确保完成调用方的撤销语义
revoke_bias(o, false, true, requesting_thread);
}
...
BiasedLocking::Condition status_code = BiasedLocking::BIAS_REVOKED;
if (attempt_rebias_of_object &&
o->mark()->has_bias_pattern() &&
klass->prototype_header()->has_bias_pattern()) {
// 参数 attempt_rebias_of_object 为 true
// 并且发生的是批量重偏向
// 而且偏向所有者没有正在持有该偏向锁,会走到这里
// 如果满足上面三个条件,则说明当前锁对象已经被撤销为了匿名偏向
// 下面直接让该锁重偏向于当前线程
markOop new_mark = markOopDesc::encode(requesting_thread, o->mark()->age(),
klass->prototype_header()->bias_epoch());
o->set_mark(new_mark); // 由于是在安全点里,所以不需要 CAS 操作
// 注意这里的返回值,这个返回值传到 fast_enter 方法后
// fast_enter 方法就直接返回了,而不会执行到后面的 slow_enter 方法
status_code = BiasedLocking::BIAS_REVOKED_AND_REBIASED;
...
}
}
...
return status_code;
}
此时,第一类情况里的第二种已经加锁完成。
剩下的所有情况,都要接着走到 slow_enter 里进行锁升级。
ObjectSynchronizer::slow_enter 源码如下:
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
markOop mark = obj->mark();
assert(!mark->has_bias_pattern(), "should not see bias pattern here");
// 无锁的情况,尝试加轻量级锁,成功后返回
if (mark->is_neutral()) {
// 设置锁记录的 displaced mark word
lock->set_displaced_header(mark);
// 尝试将锁记录的地址写入对象的 mark word
if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
TEVENT (slow_enter: release stacklock) ;
return ; // 第一类情况的第一和第三种,在这里加锁成功后返回
}
// Fall through to inflate() ...
} else
// 处理轻量级锁重入的情况
if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
assert(lock != mark->locker(), "must not re-lock the same lock");
assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
lock->set_displaced_header(NULL);
return;
}
#if 0
// The following optimization isn't particularly useful.
if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
lock->set_displaced_header (NULL) ;
return ;
}
#endif
// The object header will never be displaced to this lock,
// so it does not matter what the value is, except that it
// must be non-zero to avoid looking like a re-entrant lock,
// and must not look locked either.
lock->set_displaced_header(markOopDesc::unused_mark());
// 这里先是调用 inflate 方法将锁膨胀为重量级锁
// 然后又调用其返回对象的 enter 方法处理重量级锁的获取请求
ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}
此时,第一类情况的第一和第三种,如果在这里成功加上轻量级锁,就可以返回了。
其余情况需要接着走到 ObjectSynchronizer::inflate ,该方法会为锁对象分配 ObjectMonitor 对象(就是我们常说的重量级锁的 monitor),然后 slow_enter 调用 ObjectMonitor 的 enter 方法处理重量级锁的获取请求。
ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
// Inflate mutates the heap ...
// Relaxing assertion for bug 6320749.
assert (Universe::verify_in_progress() ||
!SafepointSynchronize::is_at_safepoint(), "invariant") ;
for (;;) {
const markOop mark = object->mark() ;
assert (!mark->has_bias_pattern(), "invariant") ;
// The mark can be in one of the following states:
// * Inflated - just return
// * Stack-locked - coerce it to inflated
// * INFLATING - busy wait for conversion to complete
// * Neutral - aggressively inflate the object.
// * BIASED - Illegal. We should never see this
// 如果已经膨胀成功,则直接返回
if (mark->has_monitor()) {
...
return inf ;
}
// 如果有其它线程先一步正在膨胀当前锁对象,当前线程只能等待其膨胀完成
if (mark == markOopDesc::INFLATING()) {
TEVENT (Inflate: spin while INFLATING) ;
ReadStableMark(object) ;
continue ;
}
// 轻量级锁膨胀的情况
if (mark->has_locker()) {
// 为锁对象分配一个 objectmonitor 对象,并初始化其值
ObjectMonitor * m = omAlloc (Self) ;
m->Recycle();
m->_Responsible = NULL ;
m->OwnerIsThread = 0 ;
m->_recursions = 0 ;
m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ; // Consider: maintain by type/class
// 尝试在对象 mark word 写入 INFLATING 标志
markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
// 写入成功,则获得膨胀资格,否则其它线程获得膨胀资格,当前线程只能等待
if (cmp != mark) {
omRelease (Self, m, true) ;
continue ; // Interference -- just retry
}
// 源码这里解释了为什么要有一个 INFLATING 状态
// We've successfully installed INFLATING (0) into the mark-word.
// This is the only case where 0 will appear in a mark-work.
// Only the singular thread that successfully swings the mark-word
// to 0 can perform (or more precisely, complete) inflation.
//
// Why do we CAS a 0 into the mark-word instead of just CASing the
// mark-word from the stack-locked value directly to the new inflated state?
// Consider what happens when a thread unlocks a stack-locked object.
// It attempts to use CAS to swing the displaced header value from the
// on-stack basiclock back into the object header. Recall also that the
// header value (hashcode, etc) can reside in (a) the object header, or
// (b) a displaced header associated with the stack-lock, or (c) a displaced
// header in an objectMonitor. The inflate() routine must copy the header
// value from the basiclock on the owner's stack to the objectMonitor, all
// the while preserving the hashCode stability invariants. If the owner
// decides to release the lock while the value is 0, the unlock will fail
// and control will eventually pass from slow_exit() to inflate. The owner
// will then spin, waiting for the 0 value to disappear. Put another way,
// the 0 causes the owner to stall if the owner happens to try to
// drop the lock (restoring the header from the basiclock to the object)
// while inflation is in-progress. This protocol avoids races that might
// would otherwise permit hashCode values to change or "flicker" for an object.
// Critically, while object->mark is 0 mark->displaced_mark_helper() is stable.
// 0 serves as a "BUSY" inflate-in-progress indicator.
// The owner can't die or unwind past the lock while our INFLATING
// object is in the mark. Furthermore the owner can't complete
// an unlock on the object, either.
// 从所有者的栈中获取 displaced mark
markOop dmw = mark->displaced_mark_helper() ;
assert (dmw->is_neutral(), "invariant") ;
m->set_header(dmw) ;
// 设置监视器的 owner 为对象 mark word 指向的锁记录
m->set_owner(mark->locker());
m->set_object(object);
guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
// 设置对象 mark word 为重量级锁状态
object->release_set_mark(markOopDesc::encode(m));
...
return m ;
}
// 无锁状态膨胀的情况
assert (mark->is_neutral(), "invariant");
ObjectMonitor * m = omAlloc (Self) ;
// prepare m for installation - set monitor to initial state
m->Recycle();
m->set_header(mark);
m->set_owner(NULL);
m->set_object(object);
m->OwnerIsThread = 1 ;
m->_recursions = 0 ;
m->_Responsible = NULL ;
m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ; // consider: keep metastats by type/class
if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
// 替换对象头的 mark word 为重量级锁状态不成功
// 说明有另外一个线程膨胀成功,释放 monitor 对象
m->set_object (NULL) ;
m->set_owner (NULL) ;
m->OwnerIsThread = 0 ;
m->Recycle() ;
omRelease (Self, m, true) ;
m = NULL ;
continue ;
}
...
return m ;
}
}
随后来到 ObjectMonitor::enter
void ATTR ObjectMonitor::enter(TRAPS) {
Thread * const Self = THREAD ;
void * cur ;
// 重量级锁的 owner 为 NULL,表示当前重量级锁是刚从无锁状态膨胀上来的
// 如果能 CAS 设置成功,则当前线程直接获得锁
cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
if (cur == NULL) {
// Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
assert (_recursions == 0 , "invariant") ;
assert (_owner == Self, "invariant") ;
// CONSIDER: set or assert OwnerIsThread == 1
return ;
}
// 重量级锁重入
if (cur == Self) {
// TODO-FIXME: check for integer overflow! BUGID 6557169.
_recursions ++ ;
return ;
}
// 如果重量级锁的 owner 指向当前线程的锁记录
// 则说明当前重量级锁是刚从当前线程持有的轻量级锁膨胀上来的
// 当前线程已经持有了该重量级锁,做一些设置后即可返回
if (Self->is_lock_owned ((address)cur)) {
assert (_recursions == 0, "internal state error");
// 初始化重入计数
_recursions = 1 ;
// 把原本指向线程锁记录的 owner,改成指向线程
_owner = Self ;
OwnerIsThread = 1 ;
return ;
}
// We've encountered genuine contention.
assert (Self->_Stalled == 0, "invariant") ;
Self->_Stalled = intptr_t(this) ;
// 先尝试自旋获取锁
if (Knob_SpinEarly && TrySpin (Self) > 0) {
assert (_owner == Self , "invariant") ;
assert (_recursions == 0 , "invariant") ;
assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
Self->_Stalled = 0 ;
return ;
}
...
// Prevent deflation at STW-time. See deflate_idle_monitors() and is_busy().
// Ensure the object-monitor relationship remains stable while there's contention.
Atomic::inc_ptr(&_count);
EventJavaMonitorEnter event;
{ // Change java thread status to indicate blocked on monitor enter.
// 更改 java 线程状态
JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);
Self->set_current_pending_monitor(this);
DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_enter()) {
JvmtiExport::post_monitor_contended_enter(jt, this);
}
OSThreadContendState osts(Self->osthread());
ThreadBlockInVM tbivm(jt);
// TODO-FIXME: change the following for(;;) loop to straight-line code.
for (;;) {
jt->set_suspend_equivalent();
// 在该方法中决定当前线程是获取锁还是进行系统调用挂起当前线程
EnterI (THREAD) ;
if (!ExitSuspendEquivalent(jt)) break ;
//
// We have acquired the contended monitor, but while we were
// waiting another thread suspended us. We don't want to enter
// the monitor while suspended because that would surprise the
// thread that suspended us.
//
_recursions = 0 ;
_succ = NULL ;
exit (false, Self) ;
jt->java_suspend_self();
}
Self->set_current_pending_monitor(NULL);
}
Atomic::dec_ptr(&_count);
assert (_count >= 0, "invariant") ;
Self->_Stalled = 0 ;
...
DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_entered()) {
JvmtiExport::post_monitor_contended_entered(jt, this);
}
if (event.should_commit()) {
event.set_klass(((oop)this->object())->klass());
event.set_previousOwner((TYPE_JAVALANGTHREAD)_previous_owner_tid);
event.set_address((TYPE_ADDRESS)(uintptr_t)(this->object_addr()));
event.commit();
}
if (ObjectMonitor::_sync_ContendedLockAttempts != NULL) {
ObjectMonitor::_sync_ContendedLockAttempts->inc() ;
}
}
最后在 ObjectMonitor::EnterI 方法里,要么当前线程获取到重量级锁,要么进行系统调用挂起当前线程。
参考:
- openjdk8 的源码
- 死磕Synchronized底层实现 系列