synchronized的实现原理?

synchronized的实现原理?
synchronized是怎么加锁的呢?
我们使用synchronized的时候,发现不用自己去lock和unlock,是因为JVM帮我们把这个事情做了。
- synchronized修饰代码块时,JVM采用
monitorenter
、monitorexit
两个指令来实现同步,monitorenter
指令指向同步代码块的开始位置,monitorexit
指令则指向同步代码块的结束位置。
反编译一段synchronized修饰代码块代码,javap -c -s -v -l SynchronizedDemo.class
,可以看到相应的字节码指令。
- synchronized修饰同步方法时,JVM采用
ACC_SYNCHRONIZED
标记符来实现同步,这个标识指明了该方法是一个同步方法。
同样可以写段代码反编译看一下。
synchronized锁住的是什么呢?
monitorenter、monitorexit或者ACC_SYNCHRONIZED都是基于Monitor实现的。
实例对象结构里有对象头,对象头里面有一块结构叫Mark Word,Mark Word指针指向了monitor。
所谓的Monitor其实是一种同步工具,也可以说是一种同步机制。在Java虚拟机(HotSpot)中,Monitor是由ObjectMonitor实现的,可以叫做内部锁,或者Monitor锁。
ObjectMonitor的工作原理:
- ObjectMonitor有两个队列:_WaitSet、_EntryList,用来保存ObjectWaiter 对象列表。
- _owner,获取 Monitor 对象的线程进入 _owner 区时, _count + 1。如果线程调用了 wait() 方法,此时会释放 Monitor 对象, _owner 恢复为空, _count - 1。同时该等待线程进入 _WaitSet 中,等待被唤醒。
ObjectMonitor() {
_header = NULL;
_count = 0; // 记录线程获取锁的次数
_waiters = 0,
_recursions = 0; //锁的重入次数
_object = NULL;
_owner = NULL; // 指向持有ObjectMonitor对象的线程
_WaitSet = NULL; // 处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; // 处于等待锁block状态的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
可以类比一个去医院就诊的例子[18]:
- 首先,患者在门诊大厅前台或自助挂号机进行挂号;
随后,挂号结束后患者找到对应的诊室就诊:
- 诊室每次只能有一个患者就诊;
- 如果此时诊室空闲,直接进入就诊;
- 如果此时诊室内有其它患者就诊,那么当前患者进入候诊室,等待叫号;
- 就诊结束后,走出就诊室,候诊室的下一位候诊患者进入就诊室。
这个过程就和Monitor机制比较相似:
- 门诊大厅:所有待进入的线程都必须先在入口Entry Set挂号才有资格;
- 就诊室:就诊室_Owner里里只能有一个线程就诊,就诊完线程就自行离开
- 候诊室:就诊室繁忙时,进入等待区(Wait Set),就诊室空闲的时候就从等待区(Wait Set)叫新的线程
所以我们就知道了,同步是锁住的什么东西:
- monitorenter,在判断拥有同步标识 ACC_SYNCHRONIZED 抢先进入此方法的线程会优先拥有 Monitor 的 owner ,此时计数器 +1。
- monitorexit,当执行完退出后,计数器 -1,归 0 后被其他进入的线程获得。