前言
由于 Monitor 基于操作系统调用,上下文切换导致开销大,在竞争不激烈时性能不算很好, 在 jdk6 之后进了系列优化。前文对优化措施进行了简单介绍,下面将一一介绍这些优化的细节,行文思路大致如下:
- 从重量级锁的优化开始讲,一是自旋锁,二是尽量避免进入 Monitor ,即使用轻量级锁
- 讲解轻量级锁及加锁解锁流程
- 轻量级锁在没有竞争时,每次重入仍然需要执行cas操作,为解决这个问题,因而产生了偏向锁
- 详细介绍偏向锁
Synchronized 锁的细节
一、自旋锁
自旋锁比较简单,逻辑在上篇也已经进行过阐述,这一篇章我们着重看下它的性能如何?
在竞争度较小的时候,重量级锁的上下文切换导致的开销相对于 CPU 处理任务的时间占比较重,此种情况下,自旋锁的性能有优势,因自旋而导致的 CPU 浪费在可接受范围内;当竞争激烈的时候,继续使用自旋锁则得不偿失,性能上比直接使用重量级锁要差,大量的等待锁的时间被浪费。
根据任务处理时间不同,自旋锁表现也不一,在任务持续时间长的情况下,自旋太久显然是对 CPU 时间片的浪费,且因任务持续时间长,在 10 此默认自旋次数的情况下,易出现自旋结束也无法获取到锁,那么此次空转就是毫无收益的性能浪费。在任务处理时间较短的情况下,显然自旋获得锁的几率要大,因此如果对要执行的任务有很明确的处理时长认知,可以根据情况适当的调整初始自旋次数,JVM 参数为:-XX:PreBlockSpin。
二、轻量级锁
根据观察,多线程中并不总是存在着竞争,使用轻量级锁避免了锁 Monitor 这繁重的数据结构,轻量级锁通常只锁一个字段(锁记录),在 HotSpot 中的实现是在当前线程的栈帧中创建锁记录结构(Lock Record)。
1.轻量级锁加锁流程
- 在当前线程的栈帧中创建 Lock Record
- 构建一个无锁状态的 Displaced Mark Word
- 将 Displaced Mark Word 存储到 Lock Record 中的 _displaced_header 属性
- CAS 更新 Displaced Mark Word 指针,注意【3】是将 Lock record 的 header 的值设置成一个 displaced mark word,【4】这一步是将当前对象头的 Mark Word 中的高30 位(全文都是只针对 32 位虚拟机来谈)指向 Lock Record 中的 header。
4.1 CAS 成功,执行同步代码块
4.2 CAS 失败,存在两种情况
3.2.1 判断是否为锁重入(关于轻量级锁的可重入有疑问,见下文)
3.2.2 锁被其他线程占有,需要竞争锁,进入锁膨胀过程 - 加锁成功的话,当前对象的 Mark Word 后两位锁标志位置为 00,余下高位作为指针存储 Lock Record 的地址
轻量级锁加锁源码如下:
// traditional lightweight locking
if (!success) {
// markOop就是对象头结构, 生成对象头,这个对象头的状态设置为无锁,生成的这个对象头就是displaced Mark word
markOop displaced = lockee->mark()->set_unlock

本文深入探讨了Java Synchronized的优化细节,包括自旋锁在竞争不激烈时的性能优势,轻量级锁的加锁解锁流程,以及偏向锁的概念和重入机制。文章指出,自旋锁在CPU资源允许的情况下能提高性能,而轻量级锁通过Lock Record实现无锁状态的快速加锁。同时,文章详细解析了偏向锁的工作原理,强调其在单线程场景下的性能提升,并讨论了批量重偏向和批量撤销的优化策略。
最低0.47元/天 解锁文章
1643

被折叠的 条评论
为什么被折叠?



