一、锁的字节码模板
1.1、先通过一张图说明synchronized (obj) {}的编译后的字节码(偷个懒,参考自这里:https://blog.csdn.net/weixin_42762133/article/details/103241439#synchronized_138)
1.2、JVM启动的时候将Java指令转成机器码,Java执行的是TemplateTable::monitorenter()生成的机器码
void TemplateTable::monitorenter(){
__ lock_object(c_rarg1);
}
1.3、如果设置JVM不允许使用偏向锁等,最初生成的指令就是比如说重量级锁(这里就不细究了,是一些繁琐的生成对寄存器操作指令的代码)
void InterpreterMacroAssembler::lock_object(Register lock_reg) {
if (UseHeavyMonitors) {
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); //锁执行的是InterpreterRuntime::monitorenter
} else {
if (UseBiasedLocking) {
//需要偏向锁
biased_locking_enter(lock_reg, obj_reg, swap_reg, tmp_reg, false, done, &slow_case);
}
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg);
}
}
二、类结构分析
2.1、理解锁的具体操作之前,要对Object头信息有基础认知(https://www.cnblogs.com/liyefei/p/13413501.html),锁的信息存放在Mark Word
2.2、Mark Word的基本结构(https://blog.csdn.net/qq_34823218/article/details/107009289),是否偏向锁:0(不支持偏向锁)|1(支持偏向锁)
三、锁的执行_monitorenter
3.1、入口
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
Handle h_obj(thread, elem->obj());
if (UseBiasedLocking) {
ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}
IRT_END
3.2、偏向锁:在实际应用运行过程中发现,“锁总是同一个线程持有,很少发生竞争”,也就是说锁总是被第一个占用他的线程拥有,这个线程就是锁的偏向线程。
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 {
//https://blog.csdn.net/dianmi2675/article/details/101491053
//Safepoint:从全局观点来看,所有线程必须在GC运行前,在一个safepoint处阻塞(block)。
BiasedLocking::revoke_at_safepoint(obj);
}
}
//执行轻量级锁
slow_enter(obj, lock, THREAD);
}
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
//读取对象头
markOop mark = obj->mark();
//mark=[0 |epoch|age|1|01] 没有锁定线程,并且允许使用偏向锁
if (mark->is_biased_anonymously() && !attempt_rebias) //!attempt_rebias不希望使用偏向锁
->bool is_biased_anonymously() const
//has_bias_pattern() 返回 true 时代表 markword 的可偏向标志 bit 位为 1 ,且对象头末尾标志为 01。
//biased_locker() == NULL 返回 true 时代表对象 Mark Word 中 bit field 域存储的 Thread Id 为空。
//https://blog.csdn.net/lengxiao1993/article/details/81568130
->return (has_bias_pattern() && (biased_locker() == NULL));
->bool has_bias_pattern() const
->return (mask_bits(value(), biased_lock_mask_in_place) == biased_lock_pattern); //biased_lock_pattern = 5
->JavaThread* biased_locker() const
->return (JavaThread*) ((intptr_t) (mask_bits(value(), ~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place))));
markOop biased_value = mark;
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); //设置为不允许使用偏向锁
markOop res_mark = obj->cas_set_mark(unbiased_prototype, mark); //通过cas设置
if (res_mark == biased_value) return BIAS_REVOKED; //偏向锁撤销成功
//has_bias_pattern() 返回 true 时代表 markword 的可偏向标志 bit 位为 1 ,且对象头末尾标志为 01。
//mark=[* |epoch|age|1|01] 没有锁定线程,并且允许使用偏向锁
else if (mark->has_bias_pattern())
Klass* k = obj->klass();
markOop prototype_header = k->prototype_header();
//对象当前的偏向状态已经过期,并且是不可偏向的,直接设置成已经撤销偏向即可
if (!prototype_header->has_bias_pattern())
return BIAS_REVOKED; //放弃偏向,走slow_enter(obj, lock, THREAD);
//实例的epoch和类本身的epoch值不一样,说明它已经过期,也就是说这个对象当前处于未偏向但是可偏向的状态(rebiasable)
else if (prototype_header->bias_epoch() != mark->bias_epoch())
//获取偏向锁成功
markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
markOop res_mark = obj->cas_set_mark(rebiased_prototype, mark);
if (res_mark == biased_value)
return BIAS_REVOKED_AND_REBIASED;
//锁存在竞争,升级锁并撤销的偏向
//启发式策略:https://blog.csdn.net/weixin_39687783/article/details/85058764
HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
if (heuristics == HR_NOT_BIASED) //5.1:偏向状态改成了不需要偏向
return NOT_BIASED;
else if (heuristics == HR_SINGLE_REVOKE)//5.2:启发式决定执行单次的撤销
if (mark->biased_locker() == THREAD && prototype_header->bias_epoch() == mark->bias_epoch())
BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD, NULL);
else
VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
//http://www.uucode.net/201406/jdk-src-vmthread-gc
//VmThread 是一个很重要的线程。jvm起来的时候 它就会起来。
//VmThead起来后,直接一个死循环,读取这个操作队列,然后一个一个去执行。
//VmThead有一个很重要的方法execute(),这个方法不真的执行VM_Operation,而是把一个op推入操作队列。
VMThread::execute(&revoke);
return revoke.status_code();
//6:等到虚拟机运行到safepoint,实际就是执行 VM_BulkRevokeBias 的doit的 bulk_revoke_or_rebias_at_safepoint方法
VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD, (heuristics == HR_BULK_REBIAS), attempt_rebias);
VMThread::execute(&bulk_revoke);
post_class_revocation_event(&event, obj->klass(), heuristics != HR_BULK_REBIAS);
return bulk_revoke.status_code();
}
3.3、轻量级锁:
轻量锁与偏向锁不同的是:
1)轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁
2)每次进入退出同步块都需要CAS更新对象头
3)争夺轻量级锁失败时,自旋尝试抢占锁
class BasicLock {
volatile markOop _displaced_header;
void move_to(oop obj, BasicLock* dest);
}
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
markOop mark = obj->mark();
//判断mark是否为无锁状态 & 不可偏向(锁标识为01,偏向锁标志位为0)
if (mark->is_neutral()) {
// 保存Mark 到 线程栈 Lock Record 的displaced_header中
lock->set_displaced_header(mark);
// CAS 将 Mark Down 更新为 指向 lock 对象的指针,成功则获取到锁
if (mark == obj()->cas_set_mark((markOop) lock, mark)) {
return;
}
// 根据对象mark 判断已经有锁 & mark 中指针指的当前线程的Lock Record(当前线程已经获取到了,不必重试获取)
} 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);
}
3.4、重量级锁:当竞争线程尝试占用轻量级锁失败多次之后,轻量级锁就会膨胀为重量级锁,重量级线程指针指向竞争线程,竞争线程也会阻塞,等待轻量级线程释放锁后唤醒他。
ObjectMonitor() {
_header = NULL;// displaced object header word - mark
_waiters = 0,// number of waiting threads
_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
FreeNext = NULL ;// Free list linkage
_EntryList = NULL ;// Threads blocked on entry or reentry.
}
//_EntryList、_cxq的类型
class ObjectWaiter : public StackObj {
ObjectWaiter * volatile _next;
ObjectWaiter * volatile _prev;
Thread* _thread;
ParkEvent * _event;
...
}
ObjectMonitor* ObjectSynchronizer::inflate(Thread * Self, oop object, const InflateCause cause) {
for (;;) {
const markOop mark = object->mark();
//判断当前对象是否已经有了监视器,其判断的方式为((value() & monitor_value) != 0),value表示头部的mark_word值,monitor_value=2,用二进制表示就是10
if (mark->has_monitor()) {//当前对象已有monitor
ObjectMonitor * inf = mark->monitor();
return inf;
}
//如果当前的锁正在膨胀中(轻量级锁膨胀为重量级锁),重新获取mark_word,进入下一次循环
if (mark == markOopDesc::INFLATING()) {
ReadStableMark(object);
continue;
}
if (mark->has_locker()) { //被轻量级锁标记
//获取或创建ObjectMonitor
ObjectMonitor * m = omAlloc(Self);//从当前线程中分配ObjectMonitor
->for (;;)
->ObjectMonitor * m;
->m = Self->omFreeList;
//a,如果当前线程中omFreeList不为空,分配当前线程omFreeList的头结点。否则进入b
->if (m != NULL)
->Self->omFreeList = m->FreeNext;
->Self->omFreeCount--;
->return m;
//b,如果全局的gFreelist不为空,从gFreelist中分配,否则进入c
->if (gFreeList != NULL)
->ObjectMonitor * take = gFreeList;
->omRelease(Self, take, false);
->m->FreeNext = Self->omFreeList;
->Self->omFreeList = m;
->continue;
//c,new一个128大小的ObjectMonitor数组,第0个元素保留, 建立freelist链表,并与gFreeList连通,gFreeList指向新建立的链表.进入下一次循环
->size_t neededsize = sizeof(PaddedEnd<ObjectMonitor>) * _BLOCKSIZE;
->size_t aligned_size = neededsize + (DEFAULT_CACHE_LINE_SIZE - 1);
->void* real_malloc_addr = (void *)NEW_C_HEAP_ARRAY(char, aligned_size, mtInternal);
->temp = (PaddedEnd<ObjectMonitor> *) align_up(real_malloc_addr, DEFAULT_CACHE_LINE_SIZE);
->for (int i = 1; i < _BLOCKSIZE; i++) //initialize the linked list, each monitor points to its next
->temp[i].FreeNext = (ObjectMonitor *)&temp[i+1]; //
->emp[0].FreeNext = gBlockList;
->temp[_BLOCKSIZE - 1].FreeNext = gFreeList;
->gFreeList = temp + 1;
m->Recycle();
//cas方式尝试膨胀,如果失败,进入下一次循环
markOop cmp = object->cas_set_mark(markOopDesc::INFLATING(), mark);
markOop dmw = mark->displaced_mark_helper();
m->set_header(dmw);
m->set_owner(mark->locker());
m->set_object(object);
return m;
}
}
}
//将线程放入等待队列,并挂起
void ObjectMonitor::enter(TRAPS) {
Thread * const Self = THREAD;
void * cur = Atomic::cmpxchg(Self, &_owner, (void*)NULL);
if (cur == Self) {
_recursions++;
return;
}
if (Self->is_lock_owned ((address)cur)) {
_recursions = 1;
_owner = Self;
return;
}
/*************** 尝试通过自旋获取锁_S ******************/
if (Knob_SpinEarly && TrySpin (Self) > 0) return;
->int ObjectMonitor::TrySpin(Thread * Self)
->int ctr = Knob_FixedSpin;
->while (--ctr >= 0)
//通过非阻塞的方式尝试加锁
->if (TryLock()) return 1;
->int Monitor::TryLock()
->intptr_t v = _LockWord.FullWord;
->for (;;)
//锁已被占用
->if ((v & _LBIT) != 0) return 0;
//如果CAS成功U未V,如果失败U为成功的那个线程的值
->const intptr_t u = Atomic::cmpxchg(v|_LBIT, &_LockWord.FullWord, v);
->if (v == u) return 1;
->v = u;
//空转CPU,相当于暂停一会
//感觉就是阻塞CPU,同时又不切换CPU执行的线程
->SpinPause();
//nop 也就是多增加几个空的机器周期,一来省电,二来本身spin lock就需要cpu空运行,并且不需要访问内存。
//https://blog.csdn.net/raintungli/article/details/7109182
->SpinPause:
rep
nop
movq $1, %rax
ret
/*************** 尝试通过自旋获取锁_E ******************/
/********** 加入等待队列,并挂起当前线程_S **************/
for (;;) {
EnterI(THREAD);
//创建ObjectWaiter并加入等待队列,_cxq指向最新一条node
->ObjectWaiter node(Self);
->for (;;)
->node._next = nxt = _cxq;
//如果CAS成功cmpxchg为nxt,如果失败cmpxchg为成功的那个线程的node
->if (Atomic::cmpxchg(&node, &_cxq, nxt) == nxt) break;
->for (;;)
//挂起线程
->Self->_ParkEvent->park();
//被唤醒,重新尝试抢锁
if (TryLock(Self) > 0) break;
}
/********** 加入等待队列,并挂起当前线程_E **************/
}
四、锁的执行_wait
4.1 注册wait函数
//Object.java
public final native void wait(long timeoutMillis) throws InterruptedException;
static JNINativeMethod methods[] = {
{"hashCode", "()I", (void *)&JVM_IHashCode},
{"wait", "(J)V", (void *)&JVM_MonitorWait},
{"notify", "()V", (void *)&JVM_MonitorNotify},
{"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll},
{"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone},
};
JNIEXPORT void JNICALL Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls) {
(*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]));
->return functions->RegisterNatives(this,clazz,methods,nMethods);
}
4.2 执行wait函数
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
ObjectSynchronizer::wait(obj, ms, CHECK);
JVM_END
int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
if (UseBiasedLocking) {
BiasedLocking::revoke_and_rebias(obj, false, THREAD);
}
ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj(), inflate_cause_wait);
monitor->wait(millis, true, THREAD);
return dtrace_waited_probe(monitor, obj, THREAD);
}
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
//省略部分代码
jt->set_current_waiting_monitor(this);//设置当前正在waiting的监视器
ObjectWaiter node(Self);//将当前线程封装成一个ObjectWaiter
Thread::SpinAcquire (&_WaitSetLock, "WaitSet - add") ;
AddWaiter(&node) ;//添加到ObjectMonitor的等待队列中_Waitset
Thread::SpinRelease(&_WaitSetLock) ;
exit(true, Self) ;
//偏向锁,为0才真正需要释放
->if (_recursions != 0) _recursions--; return;
//取出阻塞中的线程执行
->1:w = _cxq;
->2:w = _EntryList;
->ExitEpilog(Self, w);
->ParkEvent * Trigger = Wakee->_event;
->Trigger->unpark();
int ret = OS_OK ;
int WasNotified = 0 ;
{ // State transition wrappers
OSThread* osthread = Self->osthread();
OSThreadWaitState osts(osthread, true);
{
.......
if (node._notified == 0) {//如果此时没有被唤醒
if (millis <= 0) {
Self->_ParkEvent->park () ;//挂起当前线程,windows下是调用WINAPI函数waitforsingleobject
} else {
ret = Self->_ParkEvent->park (millis) ;
}
}
.......
}
.....
}
五、锁的执行_notify
5.1、弹出一个ObjectWaiter放入待执行队列,如果是policy=4,则直接唤醒ObjectWaiter中的线程
void ObjectMonitor::notify(TRAPS) {
DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
//将wait中的线程放入待执行队列,如果是policy=4,则直接唤醒
INotify(THREAD);
->ObjectWaiter * iterator = DequeueWaiter();
->ObjectWaiter * list = _EntryList;
->if (policy == 0) //Policy == 0:放入_EntryList队列的排头位置;
->else if (policy == 1) //Policy == 1:放入_EntryList队列的末尾位置;
->else if (policy == 2) //Policy == 2:_EntryList队列为空就放入_EntryList,否则放入_cxq队列的排头位置;请注意把ObjectWaiter的地址写到_cxq变量的时候要用CAS操作,因为此时可能有其他线程正在竞争锁,竞争失败的时候会将自己包装成ObjectWaiter对象加入到_cxq中;
->else if (policy == 3) //Policy == 3:放入_cxq队列中,末尾位置;
->else //Policy等于其他值,立即唤醒ObjectWaiter对应的线程;
->ParkEvent * ev = iterator->_event;
->ev->unpark();
->if (policy < 4)
->iterator->wait_reenter_begin(this);
OM_PERFDATA_OP(Notifications, inc(1));
}
六、锁的执行_monitorexit
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem))
Handle h_obj(thread, elem->obj());
ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread);
->fast_exit(object, lock, THREAD);
->ObjectSynchronizer::inflate(THREAD, object, inflate_cause_vm_internal)->exit(true, THREAD);
->void ObjectMonitor::exit(bool not_suspended, TRAPS)
//偏向锁,为0才真正需要释放
->if (_recursions != 0) _recursions--; return;
//取出阻塞中的线程执行
->1:w = _cxq;
->2:w = _EntryList;
->ExitEpilog(Self, w);
->ParkEvent * Trigger = Wakee->_event;
->Trigger->unpark();
IRT_END