首页 > 其他 > 详细

synchronized 实现原理

时间:2020-11-16 09:35:59      阅读:42      评论:0      收藏:0      [点我收藏+]

1 业务背景

雪花算法的自增序列,使用了 synchronized、AtomicLong、LongAdder 实现同步自增。
测试3个场景:

  • 单线程场景 synchronized >> LongAdder > AtomicLong
  • 低并发场景 AtomicLong > LongAdder > synchronized
  • 高并发场景 LongAdder > synchronized > AtomicLong

可以发现 synchronized 在单线程场景表现优秀,JDK6 对 synchronized 做了一系列优化。
锁消除 JVM检测到一些同步的代码块,完全不存在数据竞争的场景,也就是不需要加锁,就会进行锁消除
锁粗化 有很多操作都是对同一个对象进行加锁,就会把锁的同步范围扩展到整个操作序列之外
锁升级 根据并发度,提升锁的强度:偏向锁、轻量锁、重量锁

2 偏向锁

只有一个线程执行同步代码,线程不会主动释放偏向锁。

  1. 线程A 访问 Mark Word,确认锁标志位 01,偏向锁标识位 1,线程ID是自己,执行同步代码
  2. 线程B 访问 Mark Word,线程ID不是自己,CAS修改线程ID失败,挂起线程A
  3. 到达 safepoint,如果:
    3.1 线程A 已退出同步代码,修改线程ID 为 null
    3.2 线程A 未退出同步代码,修改锁标志位 00,升级为轻量锁

3 轻量锁

多个线程交替执行同步代码,线程尝试自旋获取锁,失败一定次数后(JVM控制,自适应)把锁升级为重量锁。

  1. 线程A 将 Mark Word 复制到本栈帧的锁记录,CAS 修改 Mark Word 指向自己的锁记录成功
  2. 线程B 将 Mark Word 复制到本栈帧的锁记录,CAS 修改 Mark Word 指向自己的锁记录失败,自旋重试
  3. 线程B 自旋一定次数后,修改锁标志位 10,升级为重量锁

4 重量锁

多个线程同时执行同步代码,阻塞取锁失败的线程。ObjectMonitor 有3个重要属性:EntryList、WaitSet、Owner。

  1. 线程A、线程B 进入同步代码块,进入 EntryList
  2. EntryList 中的线程通过 CAS 修改 Owner,成功者获取到锁,失败者 BLOCKED
  3. 线程A 调用 wait(),修改 Owner 为 null,同时进入 WaitSet 等待别的线程 notify 后重入 EntryList
  4. 线程A 释放锁,修改 Owner 为 null

5 总结

偏向锁在单线程场景下,连 CAS 都用不上,效率当然快。
轻量锁通过自旋的方式提升效率,因为 LWP 阻塞、唤醒需要上下文切换,代价很大。
网络关于 synchronized 实现原理大多只是重量锁的实现,其中所谓监控对象就是 ObjectMonitor,源码位于 src\share\vm\runtime\objectMonitor.hpp。
面试中 synchronized 还可以展开问 对象头、栈帧、ReentrantLock,所以高频是有道理的。

参考文献

synchronized 实现原理

原文:https://www.cnblogs.com/mougg/p/13983181.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!