Java通过对synchronized关键字来支持多线程同步。在字节码层面synchronized表现为monitorenter, monitorexit, 同时外加一个flags标志区分synchronized的同步作用域。synchronized用的锁是存在Java对象头里的,虚拟机的对象头主要包括两部分数据:Mark Word(标记字段)和Klass Pointer(类元数据指针),其中Klass Point是是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,Mark Word用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等。
一.同步
在生成方法entry_point时会在方法栈帧分配锁对象空间,用于存放锁。在模板表中也对应有monitorenter()模板指令的支持。Java语言层面任意对象,或类都可已当成锁。如:Object.class, this, 对象实例。HotSpot在底层通过偏向锁,轻量级锁,重量级锁三种锁支持Java语言层面的同步。
1.方法同步
对于整个方法同步,entry_point直接提供了lock_method,并在方法栈帧上分配了一个锁位
hotspot/src/cpu/x86/vm/templateInterpreterGenerator_x86.cpp
address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
if (synchronized) {
// Allocate monitor and lock method
lock_method();
}
}
hotspot/src/cpu/x86/vm/templateInterpreterGenerator_x86.cpp
void TemplateInterpreterGenerator::lock_method() {
//计算
const Address access_flags(rbx, Method::access_flags_offset());
const Address monitor_block_top(
rbp,
frame::interpreter_frame_monitor_block_top_offset * wordSize);
const int entry_size = frame::interpreter_frame_monitor_size() * wordSize;
// 获取锁对象
Label done;
__ movl(rax, access_flags);
__ testl(rax, JVM_ACC_STATIC);
// 局部变量表中第一个变量,存放着即将锁的对象指针,移动到rax中
__ movptr(rax, Address(rlocals, Interpreter::local_offset_in_bytes(0)));
__ jcc(Assembler::zero, done);
__ load_mirror(rax, rbx);
//分配和加锁
__ subptr(rsp, entry_size); //分配
__ movptr(monitor_block_top, rsp); //移动
// 存储对象指针到BasicObjectLock
__ movptr(Address(rsp, BasicObjectLock::obj_offset_in_bytes()), rax);
const Register lockreg = NOT_LP64(rdx) LP64_ONLY(c_rarg1);
__ movptr(lockreg, rsp);
//锁定
__ lock_object(lockreg);
}
2.代码块同步
hotspot/src/cpu/x86/vm/templateTable_x86.cpp
一段代码块可能有多个synchronize修饰,会在栈帧中存在多个monitor entry
void TemplateTable::monitorenter() {
//计算[monitor entry]对应的内存段
const Address monitor_block_top(
rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize);
const Address monitor_block_bot(
rbp, frame::interpreter_frame_initial_sp_offset * wordSize);
const int entry_size = frame::interpreter_frame_monitor_size() * wordSize;
Label allocated;
//当前entry信息
Register rtop = LP64_ONLY(c_rarg3) NOT_LP64(rcx);
Register rbot = LP64_ONLY(c_rarg2) NOT_LP64(rbx);
Register rmon = LP64_ONLY(c_rarg1) NOT_LP64(rdx);
// initialize entry pointer
__ xorl(rmon, rmon); // points to free slot or NULL
// 找到一个空闲entry位置
{
......
}
__ testptr(rmon, rmon); // check if a slot has been found
__ jcc(Assembler::notZero, allocated); // if found, continue with that one
// 没有找到空闲位置,则新分配一个
{
......
}
// call run-time routine
// rmon: 指向entry
__ bind(allocated);
__ increment(rbcp);
// 存储对象指针到BasicObjectLock
__ movptr(Address(rmon, BasicObjectLock::obj_offset_in_bytes()), rax);
__ lock_object(rmon);
......
__ dispatch_next(vtos);
}
二.加锁
hotspot/src/cpu/x86/vm/interp_masm_x86.cpp
如果设置了UseHeavyMonitors,则不会使用其他锁,偏置锁分配失败走slow_case,进行锁升级
void InterpreterMacroAssembler::lock_object(Register lock_reg) {
//使用重量级锁,转到InterpreterRuntime::monitorenter执行
if (UseHeavyMonitors) {
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
} else {
Label done;
Label slow_case;
//取对象指针
movptr(obj_reg, Address(lock_reg, obj_offset));
//如果使用偏执锁,走biased_locking_enter,否则走slow_case
if (UseBiasedLocking) {
biased_locking_enter(lock_reg, obj_reg, swap_reg, tmp_reg, false, done, &slow_case);
}
//......
bind(slow_case);
// Call the runtime routine for slow case
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
bind(done);
}
}
1.偏向加锁
hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp
偏向锁的锁对象保存了当前偏向的线程ID,如果是同一线程重复加锁,则只需检查线程ID,减少加锁去锁开销。如果当前线程已加锁,且锁是偏向锁,有其他线程竞争则偏向锁将被撤销。如果偏向锁过期或失效,则线程竞争偏向锁将重偏。对于轻量级锁在出现竞争时,将自旋等待。
int MacroAssembler::biased_locking_enter(Register lock_reg,
Register obj_reg,
Register swap_reg,
Register tmp_reg,
bool swap_reg_contains_mark,
Label& done,
Label* slow_case,
BiasedLockingCounters* counters) {
Address mark_addr (obj_reg, oopDesc::mark_offset_in_bytes());
// Biased locking
Label cas_label;
......
jcc(Assembler::equal, done);
Label try_revoke_bias;
Label try_rebias;
//检查是否需要撤销偏向
testptr(header_reg, markOopDesc::biased_lock_mask_in_place);
jccb(Assembler::notZero, try_revoke_bias);
//检查是否需要重偏向
testptr(header_reg, markOopDesc::epoch_mask_in_place);
jccb(Assembler::notZero, try_rebias);
//CAS获取并设置偏向锁
NOT_LP64( movptr(swap_reg, saved_mark_addr); )
andptr(swap_reg,
markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place);
#ifdef _LP64
movptr(tmp_reg, swap_reg);
orptr(tmp_reg, r15_thread);
#else
get_thread(tmp_reg);
orptr(tmp_reg, swap_reg);
#endif
if (os::is_MP()) {
lock();
}
//如果有其他线程竞争,走slow_case
cmpxchgptr(tmp_reg, mark_addr);
if (counters != NULL) {
cond_inc32(Assembler::zero,
ExternalAddress((address) counters->anonymously_biased_lock_entry_count_addr()));
}
if (slow_case != NULL) {
jcc(Assembler::notZero, *slow_case);
}
jmp(done);
bind(try_rebias);
//偏向过期,可重偏
load_prototype_header(tmp_reg, obj_reg);
#ifdef _LP64
orptr(tmp_reg, r15_thread);
#else
get_thread(swap_reg);
orptr(tmp_reg, swap_reg);
movptr(swap_reg, saved_mark_addr);
#endif
if (os::is_MP()) {
lock();
}
//如果有其他线程竞争,走slow_case
cmpxchgptr(tmp_reg, mark_addr);
if (counters != NULL) {
cond_inc32(Assembler::zero,
ExternalAddress((address) counters->rebiased_lock_entry_count_addr()));
}
if (slow_case != NULL) {
jcc(Assembler::notZero, *slow_case);
}
jmp(done);
bind(try_revoke_bias);
//撤销偏向
NOT_LP64( movptr(swap_reg, saved_mark_addr); )
load_prototype_header(tmp_reg, obj_reg);
if (os::is_MP()) {
lock();
}
cmpxchgptr(tmp_reg, mark_addr);
if (counters != NULL) {
cond_inc32(Assembler::zero,
ExternalAddress((address) counters->revoked_lock_entry_count_addr()));
}
bind(cas_label);
return null_check_offset;
}
2.撤销与重偏向
hotspot/src/share/vm/interpreter/interpreterRuntime.cpp
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
if (UseBiasedLocking) {
// Retry fast entry if bias is revoked to avoid unnecessary inflation
ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}
IRT_END
hotspot/src/share/vm/runtime/synchronizer.cpp
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock,
bool attempt_rebias, TRAPS) {
if (UseBiasedLocking) { //使用偏向锁
if (!SafepointSynchronize::is_at_safepoint()) {//不在安全点
//撤销或重偏
BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
return;
}
} else {
BiasedLocking::revoke_at_safepoint(obj);
}
}
//慢速
slow_enter(obj, lock, THREAD);
}
hotspot/src/share/vm/runtime/biasedLocking.cpp
- is_biased_anonymously为true,表明对象锁未偏向任一线程,执行撤销偏向锁
- has_bias_pattern为true,表明对象锁。处于可偏向状态
- has_bias_pattern为true,kclass对象锁与obj对象锁不一致,执行撤销
- has_bias_pattern为true,epoch过期,执行撤销
- has_bias_pattern为true,epoch过期,允许重偏向,执行撤销并重偏向
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
markOop mark = obj->mark();
if (mark->is_biased_anonymously() && !attempt_rebias) {
//对象处于可偏向状态, 且 线程ID 为空, 表示尚未偏向于任意一个线程
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);
return BIAS_REVOKED;
} else if (prototype_header->bias_epoch() != mark->bias_epoch()) { //偏向过期
if (attempt_rebias) { //进行撤销重偏
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;
}
}
}
}
//......
return bulk_revoke.status_code();
}
3.锁膨胀
hotspot/src/share/vm/runtime/synchronizer.cpp
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
markOop mark = obj->mark();
if (mark->is_neutral()) {//未持有锁,保存原来的对象头
lock->set_displaced_header(mark);
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())) {//加锁状态且是当前线程的锁
lock->set_displaced_header(NULL);
return;
}
//否则,进行锁膨胀
lock->set_displaced_header(markOopDesc::unused_mark());
ObjectSynchronizer::inflate(THREAD,
obj(),
inflate_cause_monitor_enter)->enter(THREAD);
}
hotspot/src/share/vm/runtime/synchronizer.cpp
- 对象锁是重量级锁,无法膨胀了,返回
- 对象锁正在膨胀,自旋
- 对象锁是轻量级锁,则分配一个重量级锁,设置给锁对象
- 未持有锁,分配一个重量级锁,设置给锁对象
ObjectMonitor* ObjectSynchronizer::inflate(Thread * Self,
oop object,
const InflateCause cause) {
for (;;) {
const markOop mark = object->mark();
// 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
// CASE: inflated
if (mark->has_monitor()) { //当前是重量级锁,直接返回
ObjectMonitor * inf = mark->monitor();
event.cancel(); // let's not post an inflation event, unless we did the deed ourselves
return inf;
}
//锁正在膨胀,循环等待
if (mark == markOopDesc::INFLATING()) {
continue;
}
if (mark->has_locker()) { //当前是轻量级锁,则分配一个重量级锁,设置给锁对象
ObjectMonitor * m = omAlloc(Self);
m->Recycle();
m->_Responsible = NULL;
m->_recursions = 0;
m->_SpinDuration = ObjectMonitor::Knob_SpinLimit; // Consider: maintain by type/class
markOop cmp = (markOop) Atomic::cmpxchg_ptr(markOopDesc::INFLATING(), object->mark_addr(), mark);
if (cmp != mark) {
omRelease(Self, m, true);
continue; // Interference -- just retry
}
markOop dmw = mark->displaced_mark_helper();
// Setup monitor fields to proper values -- prepare the monitor
m->set_header(dmw);
m->set_owner(mark->locker());
m->set_object(object);
object->release_set_mark(markOopDesc::encode(m));
OM_PERFDATA_OP(Inflations, inc());
TEVENT(Inflate: overwrite stacklock);
if (event.should_commit()) {
post_monitor_inflate_event(event, object, cause);
}
return m;
}
//如果走到这里,则未持有锁,分配一个重量级锁,设置给锁对象
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->_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) {
m->set_object(NULL);
m->set_owner(NULL);
m->Recycle();
omRelease(Self, m, true);
m = NULL;
continue;
}
return m;
}
}
如果一个线程获取到了偏向锁,在没有其他线程竞争的情况下,如果下次再执行该同步块时则只需要简单判断当前偏向锁所偏向的对象是否是当前线程,如果是则不需要再进行任何获取锁与释放锁的过程,直接执行同步块。当一个线程获取到该锁后,另一个线程也来获取该锁,这个线程并不会被直接阻塞,而是通过自旋来等待该锁被释放,所谓的自旋就是让线程执行一段无意义的循环。如果多个线程同时竞争锁,只会有一个线程得到这把锁,其他线程获取锁失败不会和轻量级锁进行自旋等待锁被释放,而是直接阻塞没有获取成功的线程。