synchronized原理-JVM源码

1,201 阅读2分钟

monitor监视器锁

在HotSpot虚拟机中,monitor是由ObjectMonitor实现的。其源码是用c++来实现的。

monitor竞争

当多个线程执行到同步代码块时就会产生竞争,synchronized会执行monitorenter指令,最终会调用C++的ObjectMonitor::enter方法。

竞争流程概括小结

1.通过CAS尝试把monitor的owner字段设置为当前线程。

2.如果设置之前的owner指向当前线程,说明当前线程再次进入monitor,即重入锁,执行recursions++,记录锁重入的次数。

3.如果当前线程是第一次进入该monitor,设置recursions为1,_owner为当前线程,该线程成功获得锁并返回。

4.如果获取锁失效,则进入等待队列,等待锁的释放。

monitor等待

竞争失败调用的是ObjectMonitor对象的EnterI方法

等待流程概括小结

1.当前线程被封装成ObjectWaiter对象的node,状态设置为ObjectWaiter::TS_CXQ。

2.在for循环中,通过CAS把node节点push到_cxq列表中,同一时刻可能有多个线程把自己的node节点push到cxq列表中。

3.node节点push到_cxq列表之后,通过自旋尝试获取锁,如果没有获取到锁,则通过park将当前线程挂起,等待被唤醒。

4.当线程被唤醒时,会从挂起的点继续执行,通过ObjectMonitor::TryLock尝试获取锁。

monitor释放

当某个持有锁的线程执行完同步代码块时,会进行锁的释放,给其它线程机会执行同步代码。调用的是ObjectMonitor对象的Exit方法。

释放流程概括小结

recursions对应线程的重入次数,可重入锁,当减为0时说明线程完全出了同步代码块并释放锁,同时根据不同的策略(QMode指定)去唤醒正在等待阻塞的线程,从等待链表_cxq和_EntryList中获取头结点,通过ExitEpilog方法唤醒该节点封装的线程,唤醒操作最终由UNpark操作。

monitor是重量级锁

ObjectMonitor的函数调用中会涉及到Atomic::cmpxchg_ptr,Atomic::inc_ptr等内核函数,执行同步代码块,没有竞争到说的对象会park()被挂起,竞争到锁的线程会unpark()唤醒。这个时候会存在操作系统用户态(初始进程都运行与用户空间)和内核态(调用系统调用某些操作)的切换,这种切换回会消耗大量的系统资源。