前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >synchronized的锁升级、锁膨胀

synchronized的锁升级、锁膨胀

作者头像
BUG弄潮儿
发布2020-06-15 17:46:53
1.1K0
发布2020-06-15 17:46:53
举报
文章被收录于专栏:JAVA乐园JAVA乐园

阅读文本大概需要3分钟。

0x01:偏向锁

偏向第一个拿到锁的线程。

即第一个拿到锁的线程,锁会在对象头 Mark Word 中通过 CAS 记录该线程 ID,该线程以后每次拿锁时都不需要进行 CAS(指轻量级锁)。

如果该线程正在执行同步代码块时有其他线程在竞争(指其他线程尝试 CAS 让 Mark Word 设置自己的线程 ID),会被升级为轻量级锁。

如果其他线程发现 Mark Word 里记的不是自己,且发现原持有偏向锁的线程已经执行完同步代码块,会尝试 CAS 把 Mark Word 中的改为自己的线程 ID。

0x02:轻量级锁

轻量级锁就是通过 CAS 进行加锁的。

JVM 会给线程的栈帧中创建一个叫锁记录 Lock Record 的空间,把对象头 Mark Word 复制到该空间里(Displaced Mark Word),并通过CAS 尝试把原对象头 Mark Word 中锁记录指针指向该锁记录。如果成功,表示线程拿到了锁。如果失败,则进行自旋(自旋锁),自旋超过一定次数时升级为重量级锁,这时该线程会被内核挂起。

0x03:自旋锁

轻量级锁膨胀为重量级锁前,线程在执行 monitorenter 指令进入等待队列时,会通过自旋去尝试获得锁。

如果自旋超过一定次数时还未拿到锁,就会进入阻塞状态,等待内核来调度。此时会发生内核态与用户态之间的上下文切换,所以会影响性能(引入自旋锁就是为了减少这个开销)。

因为后面的线程也先进行自旋尝试获取锁,所以这对于已被阻塞的那些线程来说,会不公平

0x04: 重量级锁

重量级锁就是通过内核来操作线程。因为频繁出现内核态与用户态的切换,会严重影响性能。

升级为重量级锁时会在堆中创建 monitor 对象,并将 Mark Word 指向该 monitor 对象。monitor 中有 cxq(ContentionList),EntryList ,WaitSet,owner:

图片来自:死磕Synchronized底层实现--重量级锁

锁升级的流程图

图片来自:Java Synchronised机制

0x05:锁降级

Hotspot 在 1.8 开始有了锁降级。在 STW 期间 JVM 进入安全点时如果发现有闲置的 monitor(重量级锁对象),会进行锁降级。

Java锁优化--JVM锁降级

0x06:为什么锁信息存放在对象头里?

死磕Synchronized底层实现--概论 中:

因为在Java中任意对象都可以用作锁,因此必定要有一个映射关系,存储该对象以及其对应的锁信息(比如当前哪个线程持有锁,哪些线程在等待)。一种很直观的方法是,用一个全局map,来存储这个映射关系,但这样会有一些问题:需要对map做线程安全保障,不同的synchronized之间会相互影响,性能差;另外当同步对象较多时,该map可能会占用比较多的内存。

所以最好的办法是将这个映射关系存储在对象头中,因为对象头本身也有一些hashcode、GC相关的数据,所以如果能将锁信息与这些信息共存在对象头中就好了。

也就是说,如果用一个全局 map 来存对象的锁信息,还需要对该 map 做线程安全处理,不同的锁之间会有影响。所以直接存到对象头。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 BUG弄潮儿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01:偏向锁
  • 0x02:轻量级锁
  • 0x03:自旋锁
  • 0x04: 重量级锁
  • 锁升级的流程图
  • 0x05:锁降级
  • 0x06:为什么锁信息存放在对象头里?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档