synchronized
锁定某个对象,才能访问某段代码
1 | public class SynchronizedTest { |
字节码实现(编译层面):通过monitorenter 和 monitorexit 实现互斥
1 | 0 getstatic #2 <com/devin/jmm/SynchronizedTest.lock : Ljava/lang/Object;> |
为什么存在两个monitorexit
1 | 同步代码块中发生异常时,synchronized自动解锁 |
JVM层级(Hotspot)
在对象上加锁,锁定的是对象
JVM层级实现其实就是锁升级的过程
JDK较早版本,锁对象是OS的资源,利用互斥量实现,获取锁和释放锁涉及到用户态和内核态的切换, 属于重量级操作,效率比较低
现代版本进行了优化
无锁 -> 偏向锁 -> 轻量级锁(自旋锁)-> 重量级锁
- 无锁 -> 偏向锁
偏向锁 - markword 上记录当前线程指针(线程id),下次同一个线程加锁的时候,不需要竞争,只需要判断线程指针是否同一个,所以偏向锁偏向加锁的第一个线程,hashCode备份在线程栈上,线程销毁时,锁降级为无锁
- 偏向锁被争用 - 升级为轻量级锁
每个线程存放LockRecord在自己的线程栈上,用CAS去争用markword的LR的指针,指针指向哪个线程,哪个线程就获得了轻量级锁
- 轻量级锁自旋次数超过10次,升级为重量级锁
如果太多线程自旋 CPU消耗过大,不如升级为重量级锁,进入对象的等待队列(不消耗CPU)
重量级锁的实现,生成一个C++层面的monitor对象,markword指向这个monitor对象(互斥量),monitor提供一个阻塞队列,竞争锁的线程入队等待,由CPU调度决定获取重量级锁的线程
偏向锁由于有锁撤销的过程revoke,会消耗系统资源,所以,在锁争用特别激烈的时候,用偏向锁未必效率高,还不如直接使用轻量级锁(-XX:-useBiasedLocking)
synchronized VS Lock(CAS)
1 | 在高争用,高耗时的环境下synchronized效率更高 |
synchronized的CPU层面实现
1 | 使用 lock + comxchg 实现 |