做单页网站,炫彩发光字制作,山东济宁网站建设设计,wordpress上传apk目录
一、前言
二、阿里开发手册
三、synchronized 锁优化的背景
四、Synchronized的性能变化
1. Java5之前#xff1a;用户态和内核态之间的切换
2. java6开始#xff1a;优化Synchronized
五、锁升级
1. 无锁
2. 偏向锁
2.1. 前言
2.2. 什么是偏向锁
2.3. 偏向…目录
一、前言
二、阿里开发手册
三、synchronized 锁优化的背景
四、Synchronized的性能变化
1. Java5之前用户态和内核态之间的切换
2. java6开始优化Synchronized
五、锁升级
1. 无锁
2. 偏向锁
2.1. 前言
2.2. 什么是偏向锁
2.3. 偏向锁的工作过程
2.4. 为什么要引入偏向锁
2.5. 什么是markword
2.6. 偏向锁默认是打开还是关闭的
2.7. 偏向锁的持有
2.8. 偏向锁JVM命令
linux命令
windows cmd命令
2.9. 偏向锁的升级
2.10. 偏向锁的的撤销
调用对象 hashCode导致偏向锁撤销
调用 wait/notify升级为重量级锁
2.11. 批量重偏向与批量撤销
批量重偏向
批量撤销
知识小结
3. 轻量级锁竞争不激烈
3.1. 为什么要引入轻量级锁
3.2. 轻量级锁的获取
3.3. 如何直接进入轻量级锁
3.4. 测试轻量级锁
3.5. 升级轻量级锁流程
3.6. 轻量级锁的核心原理
3.7. 轻量级锁什么时候升级为重量级锁
3.8. 轻量锁与偏向锁的区别和不同
4. 重量级锁竞争激烈
4.1. 前言
4.2. 自旋优化TODO
5. 各种锁优缺点、synchronized锁升级和实现原理
五、JIT编译器对锁的优化
1. 锁粗化
2. 锁消除
六、锁降级
七、扩展
1. 自旋锁持有锁的时间较短
2. 自适应自旋锁
八、演示示例锁优化的思路和方法
1. 减少锁持有时间
2. 减小锁粒度
3. 锁分离
4. 锁粗化
5. 锁消除 一、前言
高效并发是从JDK 5升级到JDK 6后一项重要的改进项HotSpot虚拟机开发团队在这个版本上花 费了大量的
资源去实现各种锁优化技术如适应性自旋Adaptive Spinning、锁消除Lock Elimination、锁膨胀
Lock Coarsening、轻量级锁Lightweight Locking、偏向锁Biased Locking等这些技术都是
为了在线程之间更高效地共享数据及解决竞争问题从而提高程序的执行效率接下来就让我们看一下锁的进
阶历程吧。
二、阿里开发手册 三、synchronized 锁优化的背景
用锁能够实现数据的安全性但是会带来性能下降。
无锁能够基于线程并行提升程序性能但是会带来安全性下降。
要在性能与安全找到平衡点jdk6引入偏向锁、轻量级锁
四、Synchronized的性能变化
1. Java5之前用户态和内核态之间的切换
java5以前只有Synchronized这个是操作系统级别的重量级操作
重量级锁假如锁的竞争比较激烈的话性能下降‘ java的线程是映射到操作系统原生线程之上的如果要阻塞或唤醒一个线程就需要操作系统介入需要在户态
与核心态之间切换这种切换会消耗大量的系统资源因为用户态与内核态都有各自专用的内存空间专用的
寄存器等用户态切换至内核态需要传递给许多变量、参数给内核内核也需要保护好用户态在切换时的一些
寄存器值、变量等以便内核态调用结束后切换回用户态继续工作。 在Java早期版本中synchronized属于重量级锁效率低下因为监视器锁monitor是依赖于底层的操
作系统的Mutex Lock来实现的挂起线程和恢复线程都需要转入内核态去完成阻塞或唤醒一个Java线程需
要操作系统切换CPU状态来完成这种状态切换需要耗费处理器时间如果同步代码块中内容过于简单这种
切换的时间可能比用户代码执行的时间还长”时间成本相对较高这也是为什么早期的synchronized效率
低的原因Java 6之后为了减少获得锁和释放锁所带来的性能消耗引入了轻量级锁和偏向锁
2. java6开始优化Synchronized
Java SE 1.6为了减少获得锁和释放锁带来的性能消耗引入了“偏向锁”和“轻量级锁”在Java SE 1.6
中锁一共有4种状态级别从低到高依次是无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态这几
个状态会随着竞争情况逐渐升级。
锁可以升级但不能降级意味着偏向锁升级成轻量级锁后不能降级成偏向锁。
这种锁升级不能降级的策略目的是为了提高获得锁和释放锁的效率 synchronized锁由对象头中的Mark Word根据锁标志位的不同而被复用及锁升级策略
五、锁升级
synchronized用的锁是存在Java对象头里的Mark Word中
锁升级功能主要依赖MarkWord中锁标志位和释放偏向锁标志位
锁的4种状态无锁状态、偏向锁状态、轻量级锁状态自旋锁 自适应自旋锁、重量级锁状态级别从低到
高锁机制就是根据资源竞争的激烈程度不断进行锁升级的过程 1. 无锁
无锁就是没有锁相对比较好理解。
maven引入JOL
!--JOL Java Object Layout Java对象布局--
dependencygroupIdorg.openjdk.jol/groupIdartifactIdjol-core/artifactIdversion0.9/version/dependencyimport org.openjdk.jol.info.ClassLayout;public class MyObject {public static void main(String[] args) {Object o new Object();System.out.println(ClassLayout.parseInstance(o).toPrintable());}
}
运行结果
java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal 4 bytes external 4 bytes total
01 00 00 00 (00000001 00000000 00000000 00000000) (1)
00000001 00000000 00000000 00000000 二进制需要倒着看每8个位看做一个整体 1 2 3 4 变成 4 3 2 1
00000000 00000000 00000000 00000(001) 此时hashcode是0
因为懒加载的缘故使用到hashcode才会初始化
import org.openjdk.jol.info.ClassLayout;public class MyObject {public static void main(String[] args) {Object o new Object();System.out.println(10进制hash码o.hashCode());System.out.println(16进制hash码Integer.toHexString(o.hashCode()));System.out.println(2进制hash码Integer.toBinaryString(o.hashCode()));System.out.println(ClassLayout.parseInstance(o).toPrintable());}
}
运行结果
10进制hash码1265094477
16进制hash码4b67cf4d
2进制hash码1001011011001111100111101001101
java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 01 4d cf 67 (00000001 01001101 11001111 01100111) (1741638913)4 4 (object header) 4b 00 00 00 (01001011 00000000 00000000 00000000) (75)8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal 4 bytes external 4 bytes total
00000001 01001101 11001111 01100111 01001011
倒着看 31位
0(1001011 01100111 11001111 01001101) 00000001
2进制hash码 1001011011001111100111101001101
0(1001011 01100111 11001111 01001101) 00000001
去掉后8位
01001101 11001111 01100111 01001011
hashcode是31位
1001101 11001111 01100111 01001011
2. 偏向锁
2.1. 前言
偏向锁偏向于第一个获得它的线程默认不存在锁竞争的情况下常常是一个线程多次获得同一个锁重复获取
同一把锁不会再进行锁的竞争看看多线程卖票同一个线程获得体会一下
/*** description: 实现3个售票员卖出50张票的案例*/
public class SaleTicket {public static void main(String[] args) {Ticket ticket new Ticket();new Thread(() - {//循环100次保证能够卖光票for (int i 0; i 100; i) {ticket.saleTicket();}}, T1).start();new Thread(() - {for (int i 0; i 100; i) {ticket.saleTicket();}}, T2).start();new Thread(() - {for (int i 0; i 100; i) {ticket.saleTicket();}}, T3).start();}
}/*** description: 资源类*/
class Ticket {private int count 50;public synchronized void saleTicket() {if (count 0) {count--;System.out.println(Thread.currentThread().getName() 卖票成功还剩 count 张票);}}
}
运行结果
T1卖票成功还剩49张票
T1卖票成功还剩48张票
T1卖票成功还剩47张票
T1卖票成功还剩46张票
T1卖票成功还剩45张票
T1卖票成功还剩44张票
T1卖票成功还剩43张票
T1卖票成功还剩42张票
T1卖票成功还剩41张票
T1卖票成功还剩40张票
T1卖票成功还剩39张票
T1卖票成功还剩38张票
T1卖票成功还剩37张票
T1卖票成功还剩36张票
T1卖票成功还剩35张票
T1卖票成功还剩34张票
T1卖票成功还剩33张票
T1卖票成功还剩32张票
T1卖票成功还剩31张票
T1卖票成功还剩30张票
T1卖票成功还剩29张票
T1卖票成功还剩28张票
T1卖票成功还剩27张票
T1卖票成功还剩26张票
T1卖票成功还剩25张票
T1卖票成功还剩24张票
T1卖票成功还剩23张票
T1卖票成功还剩22张票
T1卖票成功还剩21张票
T1卖票成功还剩20张票
T1卖票成功还剩19张票
T1卖票成功还剩18张票
T1卖票成功还剩17张票
T1卖票成功还剩16张票
T1卖票成功还剩15张票
T1卖票成功还剩14张票
T1卖票成功还剩13张票
T1卖票成功还剩12张票
T1卖票成功还剩11张票
T1卖票成功还剩10张票
T1卖票成功还剩9张票
T1卖票成功还剩8张票
T1卖票成功还剩7张票
T1卖票成功还剩6张票
T1卖票成功还剩5张票
T1卖票成功还剩4张票
T1卖票成功还剩3张票
T1卖票成功还剩2张票
T1卖票成功还剩1张票
T1卖票成功还剩0张票
发现全是t1卖出
这样就是偏向锁的情况
2.2. 什么是偏向锁
偏向锁跟synchronized有关系当锁对象第一次被线程A获取的时候会记录线程A的id之后在没有别的线
程获取锁对象的前提下线程A在执行这个锁对应的同步代码块时不会再进行任何同步操作即这个对象锁一
直偏向线程A这就是偏向锁。
比如更衣室中有很多衣柜你在其中一个衣柜上写了你的名字当下次要使用的时候发现衣柜上仍然是你的名
字此时直接使用即可这就省去了上锁和用钥匙开锁的过程。
Jdk6开始默认开启偏向锁。当线程获取锁资源时只有第一次使用 CAS 将线程 ID 设置到对象的 Mark
Word 头之后发现这个线程 ID 是自己的就表示没有竞争不用重新 CAS。以后只要不发生竞争这个对象
就归该线程所有。
当锁释放之后另一线程获得锁资源则偏向锁升级为轻量级锁。若锁还未释放另一线程就来竞争锁资源则偏向锁直接升级为重量级锁。调用 wait/notify方法时偏向锁直接升级为重量级锁因为只有重量级锁才有该方法。调用hashcode方法因为没有空间存储hashcode偏向锁自动撤销变为无锁状态。
2.3. 偏向锁的工作过程
锁对象第一次被线程A获取的时候jvm利用cas操作将线程id写入到锁对象的mark word中此时锁会偏向线
程A。当有线程B来争抢锁的时候会先查看拥有偏向锁的线程A是否存活如果A已经结束或者不在同步代码
块中会将锁对象的标记改为无锁状态然后升级为轻量级锁。
如果A仍然存活且在同步代码块中偏向锁会升级为轻量级锁A仍然会持有锁。
偏向锁的撤销是会耗费资源的在jdk15中将其废弃https://openjdk.java.net/jeps/374
2.4. 为什么要引入偏向锁
Hotspot 的作者经过研究发现大多数情况下
多线程的情况下锁不仅不存在多线程竞争还存在锁由同一线程多次获得的情况
为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获取锁时会在对象头和栈帧中的锁
记录里存储锁偏向的线程ID以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁只需简单
地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功表示线程已经获得了
锁。如果测试失败则需要再测试一下Mark Word中偏向锁的标识是否设置成1表示当前是偏向锁如果
没有设置则使用CAS竞争锁如果设置了则尝试使用CAS将对象头的偏向锁指向当前线程。偏向锁就是在
这种情况下出现的它的出现是为了解决只有在一个线程执行同步时提高性能。即为了降低获取锁的代价才
引入的偏向锁。 通过CAS方式修改markword中的线程ID
2.5. 什么是markword
HotSpot虚拟机的对象头Object Header分为两部分
第一 部分用于存储对象自身的运行时数据
如哈希码HashCode、GC分代年龄Generational GC Age 等。
这部分数据的长度在32位和64位的Java虚拟机中分别会占用32个或64个比特官方称它为“Mark Word”。
这部分是实现轻量级锁和偏向锁的关键。
另外一部分用于存储指向方法区对象类型数据的指针
如果是数组对象还会有一个额外的部分用于存储数组长度。
这些对象内存布局的详细内容我们已经在第2章中学习过在此不再赘述只针对锁的角度做进一步细化。
由于对象头信息是与对象自身定义的数据无关的额外存储成本考虑到Java虚拟机的空间使用效 率
Mark Word被设计成一个非固定的动态数据结构以便在极小的空间内存储尽量多的信息。
它会根据对象的状态复用自己的存储空间。
例如在32位的HotSpot虚拟机中对象未被锁定的状态下Mark Word的32个比特空间里的25个比特将用于
存储对象哈希码4个比特用于存储对象分代年龄2个比特用于存储锁标志位还有1个比特固定为0这表
示未进入偏向模式。
对象除了未被锁定的正 常状态外还有轻量级锁定、重量级锁定、GC标记、可偏向等几种不同状态这些状
态下对象头的存 储内容如表所示。
HotSpot虚拟机对象头Mark Word 2.6. 偏向锁默认是打开还是关闭的
实际上偏向锁在JDK1.6之后是默认开启的但是启动时间有延迟如有必要可以使用JVM参数来关闭延迟
所以需要添加参数-XX:BiasedLockingStartupDelay0让其在程序启动时立刻启动。
开启偏向锁可以通过这个JVM参数-XX:UseBiasedLocking -XX:BiasedLockingStartupDelay0
如果你确定应用程序里所有的锁通常情况下处于竞争状态可以通过JVM参数关闭偏向锁关闭之后程序默认
会直接进入轻量级锁状态。
那么程序默认会进入轻量级锁状态可以通过-XX:-UseBiasedLocking这个JVM参数关闭偏向锁。
2.7. 偏向锁的持有
理论落地
在实际应用运行过程中发现“锁总是同一个线程持有很少发生竞争”也就是说锁总是被第一个占用他的
线程拥有这个线程就是锁的偏向线程。
那么只需要在锁第一次被拥有的时候记录下偏向线程ID。
这样偏向线程就一直持有着锁(后续这个线程进入和退出这段加了同步锁的代码块时不需要再次加锁和释放
锁。
而是直接比较对象头里面是否存储了指向当前线程的偏向锁)。
如果相等表示偏向锁是偏向于当前线程的就不需要再尝试获得锁了直到竞争发生才释放锁。
以后每次同步检查锁的偏向线程ID与当前线程ID是否一致如果一致直接进入同步。无需每次加锁解锁都去
CAS更新对象头。如果自始至终使用锁的线程只有一个很明显偏向锁几乎没有额外开销性能极高。
假如不一致意味着发生了竞争锁已经不是总是偏向于同一个线程了这时候可能需要升级变为轻量级锁才
能保证线程间公平竞争锁。
偏向锁只有遇到其他线程尝试竞争偏向锁时持有偏向锁的线程才会释放锁线程是不会主动释放偏向锁的。
技术实现
一个synchronized方法被一个线程抢到了锁时那这个方法所在的对象就会在其所在的Mark Word中将偏向
锁修改状态位同时还会有占用前54位来存储线程指针作为标识。若该线程再次访问同一个synchronized方
法时该线程只需去对象头的Mark Word 中去判断一下是否有偏向锁指向本身的ID无需再进入 Monitor
去竞争对象了。 举例说明
偏向锁的操作不用直接捅到操作系统不涉及用户到内核转换不必要直接升级为最高级我们以一个account对
象的“对象头”为例 假如有一个线程执行到synchronized代码块的时候JVM使用CAS操作把线程指针ID记录到Mark Word当中并
修改标偏向标示标示当前线程就获得该锁。锁对象变成偏向锁通过CAS修改对象头里的锁标志位字面意思
是“偏向于第一个获得它的线程”的锁。执行完同步代码块后线程并不会主动释放偏向锁。 这时线程获得了锁可以执行同步代码块。当该线程第二次到达同步代码块时会判断此时持有锁的线程是否还
是自己持有锁的线程ID也在对象头里JVM通过account对象的Mark Word判断当前线程ID还在说
明还持有着这个对象的锁就可以继续进入临界区工作。 由于之前没有释放锁这里也就不需要重新加锁。 如果自始至终使用锁的线程只有一个很明显偏向锁几乎
没有额外开销性能极高。 结论JVM不用和操作系统协商设置Mutex(争取内核)它只需要记录下线程ID就标示自己获得了当前锁不
用操作系统接入。 上述就是偏向锁在没有其他线程竞争的时候一直偏向偏心当前线程当前线程可以一直执行。
2.8. 偏向锁JVM命令
查出BiasedLock相关的参数设置
java -XX:PrintFlagsInitial |grep BiasedLock*
linux命令 windows cmd命令 默认偏向锁是打开的 UseBiasedLocking true
但是BiasedLockingStartupDelay 4000 偏向锁 启动时间有延迟
* 实际上偏向锁在JDK1.6之后是默认开启的但是启动时间有延迟
* 如有必要可以使用JVM参数来关闭延迟
* 所以需要添加参数-XX:BiasedLockingStartupDelay0让其在程序启动时立刻启动。
*
* 开启偏向锁
* -XX:UseBiasedLocking -XX:BiasedLockingStartupDelay0
*
* 如果你确定应用程序里所有的锁通常情况下处于竞争状态可以通过JVM参数关闭偏向锁
* 关闭偏向锁关闭之后程序默认会直接进入------------------------------------------ 轻量级锁状态。那么程序默认会进入轻量级锁状态
* -XX:-UseBiasedLocking
1使用默认设置 默认有延迟时间
import org.openjdk.jol.info.ClassLayout;public class MyObject {public static void main(String[] args) {Object object new Object();new Thread(() - {synchronized (object) {System.out.println(ClassLayout.parseInstance(object).toPrintable());}}, t1).start();}
}
运行结果
java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 60 f6 ef cd (01100000 11110110 11101111 11001101) (-839911840)4 4 (object header) b5 00 00 00 (10110101 00000000 00000000 00000000) (181)8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal 4 bytes external 4 bytes total
由于延迟4秒钟锁信息显示出来的是不正确的 后8位字节01100000是轻量级锁状态
2设置延迟时间为0 -XX:BiasedLockingStartupDelay0
import org.openjdk.jol.info.ClassLayout;public class MyObject {public static void main(String[] args) {Object object new Object();new Thread(() - {synchronized (object) {System.out.println(ClassLayout.parseInstance(object).toPrintable());}}, t1).start();}
}
运行结果
java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 05 70 f3 b2 (00000101 01110000 11110011 10110010) (-1292668923)4 4 (object header) 2a 02 00 00 (00101010 00000010 00000000 00000000) (554)8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal 4 bytes external 4 bytes total
后8位00000101 锁信息 101 即偏向锁
1表示为偏向锁位
01表示为锁标记位 2.9. 偏向锁的升级
一个线程持有锁第二个线程加入锁竞争
如果在运行过程中遇到了其他线程抢占锁则持有偏向锁的线程会被挂起JVM会将偏向锁升级为轻量级
锁。
2.10. 偏向锁的的撤销
当有另外线程逐步来竞争锁的时候就不能再使用偏向锁了要升级为轻量级锁
竞争线程尝试CAS更新对象头失败会等待到全局安全点STW此时不会执行任何代码撤销偏向锁。
偏向锁使用一种等到竞争出现才释放锁的机制只有当其他线程竞争锁时持有偏向锁的原来线程才会释放
锁。
偏向锁的撤销需要等待全局安全点(在这个时间点上没有正在执行的字节码 JVM中的STW的概念 )
它会首先暂停拥有偏向锁的线程 同时检查持有偏向锁的线程是否还在执行
① 第一个线程正在执行synchronized方法(处于同步块)它还没有执行完其它线程来抢夺暂停第一个线
程该偏向锁会被取消掉并出现锁升级。
此时轻量级锁由原持有偏向锁的线程持有继续执行其同步代码而正在竞争的线程会进入自旋等待获得该轻
量级锁。
② 第一个线程执行完成synchronized方法(退出同步块)第二个线程执行重新偏向 。
如果第一个线程退出当第一个线程不存在了第二个线程执行CAS将当前线程指针修改仍为偏向锁状态
如果第一个线程退出还要再次执行第二个线程也要执行那么两个线程就抢夺轻量级锁没抢到的进行自旋 此时升级为轻量级锁
调用对象 hashCode导致偏向锁撤销
调用了对象的 hashCode但偏向锁的对象 MarkWord 中存储的是线程 id如果调用 hashCode 会导致偏向锁
被撤销变为无锁状态轻量级锁会在锁记录中记录 hashCode
重量级锁会在 Monitor 中记录 hashCode
在获得偏向锁后调用 hashCode
import org.openjdk.jol.info.ClassLayout;/*** description: -XX:BiasedLockingStartupDelay0*/
public class RedoLockDemo {public static void main(String[] args) {Object object new Object();new Thread(() - {synchronized (object) {System.out.println(ClassLayout.parseInstance(object).toPrintable());}object.hashCode();System.out.println(ClassLayout.parseInstance(object).toPrintable());}, t1).start();}
}
java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 05 f8 5d dd (00000101 11111000 01011101 11011101) (-581044219)4 4 (object header) 4b 02 00 00 (01001011 00000010 00000000 00000000) (587)8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal 4 bytes external 4 bytes totaljava.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 01 ea 67 be (00000001 11101010 01100111 10111110) (-1100486143)4 4 (object header) 66 00 00 00 (01100110 00000000 00000000 00000000) (102)8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal 4 bytes external 4 bytes total
101 偏向锁变为001无锁
调用 wait/notify升级为重量级锁
如果线程获得锁后调用Object#wait方法则会将线程加入到WaitSet中当被Object#notify唤醒后会将线程
从WaitSet移动到cxq或EntryList中去。
一个ObjectMonitor对象包括这么几个关键字段cxq下图中的ContentionListEntryList WaitSet
owner。
其中cxq EntryList WaitSet都是由ObjectWaiter的链表结构owner指向持有锁的线程。 需要注意的是当调用一个锁对象的wait或notify方法时
如当前锁的状态是偏向锁或轻量级锁则会先膨胀成重量级锁。
import org.openjdk.jol.info.ClassLayout;import java.util.concurrent.TimeUnit;public class RedoLockDemo {public static void main(String[] args) {Object object new Object();Thread t1 new Thread(() - {synchronized (object) {try {System.out.println(ClassLayout.parseInstance(object).toPrintable());object.wait();System.out.println(被唤醒);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(ClassLayout.parseInstance(object).toPrintable());}, t1);t1.start();new Thread(() - {try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}synchronized (object) {object.notifyAll();System.out.println(notifyAll);}}, t2).start();}
}
运行结果
java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 05 40 22 62 (00000101 01000000 00100010 01100010) (1646411781)4 4 (object header) ec 01 00 00 (11101100 00000001 00000000 00000000) (492)8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal 4 bytes external 4 bytes totalnotifyAll
java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) da 9d 38 61 (11011010 10011101 00111000 01100001) (1631100378)4 4 (object header) ec 01 00 00 (11101100 00000001 00000000 00000000) (492)8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal 4 bytes external 4 bytes total
从101偏向锁变为010重量级锁 2.11. 批量重偏向与批量撤销
通过JVM的默认参数值找一找批量重偏向和批量撤销的阈值。
设置JVM参数-XX:PrintFlagsFinal在项目启动时即可输出JVM的默认参数值
intx BiasedLockingBulkRebiasThreshold 20 默认偏向锁批量重偏向阈值
intx BiasedLockingBulkRevokeThreshold 40 默认偏向锁批量撤销阈值
当然我们可以通过-XX:BiasedLockingBulkRebiasThreshold 和 -XX:BiasedLockingBulkRevokeThreshold 来手
动设置阈值 批量重偏向
如果对象虽然被多个线程访问但没有竞争这时偏向了线程 t1 的对象仍有机会重新偏向t2重偏向会重置对象
的 Thread ID
当撤销偏向锁阈值超过 20 次后jvm 会这样觉得我是不是偏向错了呢于是在接下来的访问中会在给这些对
象加锁时重新偏向至加锁线程 t2
例如当一个线程t1创建了大量Dog对象并执行了初始的同步操作后来另一个线程t2也将这些Dog对象作为锁对象
进行操作注意由于要输出多次JOL打印的二进制信息这里我将jol的源码做了些修改新增了只输出简化的二
进制信息toPrintable2()并且修正了
二进制打印的顺序然后再打成jar包在本地引入
import org.openjdk.jol.info.ClassLayout;import java.util.Vector;
import java.util.concurrent.TimeUnit;/*** description: -XX:BiasedLockingStartupDelay0*这里我将jol的源码新增了只输出简化的二进制信息toPrintable2()并且修正了二进制顺序然后再打成jar包在本地引入*/
class Dog {
}public class RedoLockDemo2 {public static void main(String[] args) {VectorDog list new Vector();Thread t1 new Thread(() - {for (int i 0; i 30; i) {Dog d new Dog();list.add(d);synchronized (d) {System.out.println(ClassLayout.parseInstance(d).toPrintable2());}}synchronized (list) {list.notify();//唤醒t2}}, t1);t1.start();Thread t2 new Thread(() - {synchronized (list) {try {list.wait();} catch (InterruptedException e) {e.printStackTrace();}}for (int i 0; i 30; i) {Dog d list.get(i);System.out.println(i 加锁前 ClassLayout.parseInstance(d).toPrintable2());synchronized (d) {System.out.println(i 加锁中 ClassLayout.parseInstance(d).toPrintable2());}System.out.println(i 加锁后 ClassLayout.parseInstance(d).toPrintable2());}}, t2);t2.start();}
}运行结果
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
0加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
0加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
0 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
1加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
1加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
1 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
2加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
2加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
2 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
3加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
3加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
3 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
4加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
4加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
4 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
5加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
5加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
5 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
6加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
6加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
6 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
7加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
7加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
7 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
8加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
8加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
8 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
9加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
9加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
9 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
10加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
10加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
10 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
11加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
11加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
11 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
12加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
12加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
12 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
13加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
13加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
13 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
14加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
14加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
14 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
15加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
15加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
15 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
16加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
16加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
16 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
17加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
17加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
17 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
18加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
18加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
18 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
19加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
19加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
19 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
20加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
20加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
20 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
21加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
21加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
21 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
22加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
22加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
22 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
23加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
23加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
23 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
24加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
24加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
24 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
25加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
25加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
25 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
26加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
26加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
26 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
27加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
27加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
27 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
28加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
28加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
28 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
29加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
29加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
29 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
可以看出当t1线程执行时30次循环将这30个dog对象的线程指针指向t1前54位为t1的Thread ID
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
当t2线程执行时第一次循环 加锁前dog对象0偏向t1线程
此时t1线程还存在加锁时将偏向锁升级为轻量级锁
加锁后释放锁重置为无锁状态
0加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
0加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
0 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
intx BiasedLockingBulkRebiasThreshold 20 默认偏向锁批量重偏向阈值
当循环执行到20次时 将dog对象19的偏向指向t1修改为直接指向t2就不再变为轻量级锁仍为偏向锁
达到偏向锁批量重偏向的阈值之后的dog对象全都变为重偏向t2而不再是t1
18加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
18加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
18 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
19加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
19加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
19 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
20加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
20加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
20 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
批量撤销
当撤销偏向锁阈值超过 40 次后jvm 会这样觉得自己确实偏向错了根本就不该偏向。
于是整个类的所有对象都会变为不可偏向的新建的对象也是不可偏向的
import org.openjdk.jol.info.ClassLayout;import java.util.Vector;
import java.util.concurrent.locks.LockSupport;/*** description: -XX:BiasedLockingStartupDelay0*/
class Dog {
}public class RedoLockDemo2 {static Thread t1, t2, t3;public static void main(String[] args) throws InterruptedException {int loopNumber 39;VectorDog list new Vector();t1 new Thread(() - {for (int i 1; i loopNumber; i) {Dog d new Dog();list.add(d);synchronized (d) {System.out.println(ClassLayout.parseInstance(d).toPrintable2());}}LockSupport.unpark(t2);}, t1);t1.start();t2 new Thread(() - {LockSupport.park();for (int i 1; i loopNumber; i) {Dog d list.get(i);System.out.println(i 加锁前 ClassLayout.parseInstance(d).toPrintable2());synchronized (d) {System.out.println(i 加锁中 ClassLayout.parseInstance(d).toPrintable2());}System.out.println(i 加锁后 ClassLayout.parseInstance(d).toPrintable2());}LockSupport.unpark(t3);}, t2);t2.start();t3 new Thread(() - {LockSupport.park();for (int i 1; i loopNumber; i) {Dog d list.get(i);System.out.println(i 加锁前 ClassLayout.parseInstance(d).toPrintable2());synchronized (d) {System.out.println(i 加锁中 ClassLayout.parseInstance(d).toPrintable2());}System.out.println(i 加锁后 ClassLayout.parseInstance(d).toPrintable2());}}, t3);t3.start();t3.join();System.out.println(ClassLayout.parseInstance(new Dog()).toPrintable2());}
}
运行结果
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
-----------
0加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
0加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
0 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
1加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
1加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
1 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
2加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
2加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
2 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
3加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
3加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
3 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
4加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
4加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
4 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
5加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
5加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
5 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
6加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
6加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
6 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
7加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
7加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
7 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
8加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
8加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
8 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
9加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
9加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
9 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
10加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
10加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
10 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
11加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
11加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
11 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
12加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
12加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
12 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
13加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
13加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
13 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
14加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
14加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
14 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
15加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
15加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
15 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
16加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
16加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
16 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
17加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
17加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
17 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
18加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
18加锁中 00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000
18 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
19加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
19加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
19 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
20加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
20加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
20 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
21加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
21加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
21 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
22加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
22加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
22 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
23加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
23加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
23 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
24加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
24加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
24 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
25加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
25加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
25 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
26加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
26加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
26 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
27加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
27加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
27 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
28加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
28加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
28 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
29加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
29加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
29 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
30加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
30加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
30 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
31加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
31加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
31 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
32加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
32加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
32 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
33加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
33加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
33 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
34加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
34加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
34 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
35加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
35加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
35 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
36加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
36加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
36 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
37加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
37加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
37 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
38加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101
38加锁中 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
38 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
-----------
0加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
0加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
0 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
1加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
1加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
1 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
2加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
2加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
2 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
3加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
3加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
3 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
4加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
4加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
4 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
5加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
5加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
5 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
6加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
6加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
6 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
7加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
7加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
7 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
8加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
8加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
8 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
9加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
9加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
9 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
10加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
10加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
10 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
11加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
11加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
11 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
12加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
12加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
12 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
13加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
13加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
13 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
14加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
14加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
14 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
15加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
15加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
15 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
16加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
16加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
16 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
17加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
17加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
17 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
18加锁前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
18加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
18 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
19加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
19加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
19 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
20加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
20加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
20 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
21加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
21加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
21 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
22加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
22加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
22 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
23加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
23加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
23 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
24加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
24加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
24 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
25加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
25加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
25 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
26加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
26加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
26 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
27加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
27加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
27 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
28加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
28加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
28 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
29加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
29加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
29 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
30加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
30加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
30 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
31加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
31加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
31 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
32加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
32加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
32 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
33加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
33加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
33 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
34加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
34加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
34 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
35加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
35加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
35 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
36加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
36加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
36 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
37加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
37加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
37 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
38加锁前 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101
38加锁中 00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000
38 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
前半部分t1、t2和批量重偏向一样
t2前20个对象并没有触发批量重偏向机制线程t2执行释放同步锁后转变为无锁形态 共撤销了偏向锁20
次
t2第20~40个对象触发了批量重偏向机制对象为偏向锁状态偏向线程t2
t3前20个对象并没有触发批量重偏向机制线程t3执行释放同步锁后转变为无锁形态 共撤销了偏向锁20
次
intx BiasedLockingBulkRevokeThreshold 40 默认偏向锁批量撤销阈值
此时一共撤销偏向锁40次达到批量撤销的阈值
t3第20~40个对象触发批量撤销机制此时对象锁膨胀变为轻量级锁。
设置-XX:BiasedLockingStartupDelay0后创建的新对象本该为偏向锁状态
在经历过批量重偏向和批量撤销后直接在实例化后转为无锁状态。
00000000 00000000 00000000 00000000 00000000 00000000 00000000
知识小结
批量重偏向和批量撤销是针对类的优化和对象无关。偏向锁批量重偏向一次之后不可再次批量重偏向。当某个类已经触发批量撤销机制后JVM会默认当前类产生了严重的问题剥夺了该类的新实例对象使用偏向锁的权利
3. 轻量级锁竞争不激烈
3.1. 为什么要引入轻量级锁
在竞争锁对象的线程不多而且线程持有锁的时间也不长的情景。
即一个对象虽然有多个线程访问但是多个线程的访问是错开的存在少量竞争会不存在竞争
轻量级锁是JDK 6时加入的新型锁机制它名字中的“轻量级”是相对于使用操作系统互斥量来实 现的传统锁
而言的因此传统的锁机制就被称为“重量级”锁。轻量级锁并不是用来代替重量级锁的它设计的初衷是在
没有多线程竞争的前提下减少传统的重量级锁使用操作系统互斥量产生的性能消耗。 要理解轻量级锁必
须要对HotSpot虚拟机对象的内存布局尤其是对象头部分有所了解。
3.2. 轻量级锁的获取
轻量级锁是为了在线程近乎交替执行同步块时提高性能。
主要目的 在没有多线程竞争的前提下通过CAS减少重量级锁使用操作系统互斥量产生的性能消耗说白了
先自旋再阻塞。
升级时机 当关闭偏向锁功能或多线程竞争偏向锁会导致偏向锁升级为轻量级锁
假如线程A已经拿到锁这时线程B又来抢该对象的锁由于该对象的锁已经被线程A拿到当前该锁已是偏向
锁了。而线程B在争抢时发现对象头Mark Word中的线程ID不是线程B自己的线程ID(而是线程A)那线程B就
会进行
CAS操作希望能获得锁。 此时线程B操作中有两种情况
如果锁获取成功直接替换Mark Word中的线程ID为B自己的ID(A → B)重新偏向于其他线程(即将偏向锁交
给其他线程相当于当前线程被释放了锁)该锁会保持偏向锁状态A线程OverB线程上位 如果锁获取失败则偏向锁升级为轻量级锁此时轻量级锁由原持有偏向锁的线程持有继续执行其同步代
码而正在竞争的线程B会进入自旋等待获得该轻量级锁。 3.3. 如何直接进入轻量级锁
如果关闭偏向锁就可以直接进入轻量级锁
-XX:-UseBiasedLocking
3.4. 测试轻量级锁
如果关闭偏向锁就可以直接进入轻量级锁
-XX:-UseBiasedLocking package com.dongguo.lockupgrade;import org.openjdk.jol.info.ClassLayout;/*** author Dongguo* date 2023/9/8 0008-21:46* description:*/
public class MyObject {public static void main(String[] args) {Object object new Object();new Thread(() - {synchronized (object) {System.out.println(ClassLayout.parseInstance(object).toPrintable());}}, t1).start();}
}
运行结果
java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 40 ef 6f 0a (01000000 11101111 01101111 00001010) (175107904)4 4 (object header) 6e 00 00 00 (01101110 00000000 00000000 00000000) (110)8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal 4 bytes external 4 bytes total
后8位01000000
轻量级锁 最后两位00
3.5. 升级轻量级锁流程 3.6. 轻量级锁的核心原理
轻量级锁的执行过程在抢锁线程进入临界区之前如果内置锁临界区的同步对象没有被锁定JVM首先将
在抢锁线程的栈帧中建立一个锁记录LockRecord用于存储对象目前Mark Word的拷贝,然后抢锁线程将使
用CAS自旋操作尝试将内置锁对象头的Mark Word的ptr_to_lock_record锁记录指针更新为抢锁线程栈帧
中锁记录的地址
3.7. 轻量级锁什么时候升级为重量级锁
在轻量级锁状态下继续锁竞争没有抢到锁的线程将自旋即不停地循环判断锁是否能够被成功获取。自旋
有限度的有个计数器记录自旋次数默认允许循环10次可以通过虚拟机参数更改。
java6之前是自旋锁
默认启用默认情况下自旋的次数是 10 次
-XX:PreBlockSpin10来修改
或者自旋线程数超过cpu核数一半
Java6之后 是自适应自旋锁
自适应意味着自旋的次数不是固定不变的
根据同一个锁上一次自旋的时间。和拥有锁线程的状态来决定。
长时间的自旋操作是非常消耗资源的一个线程持有锁其他线程就只能在原地空耗CPU执行不了任何有效
的任务这种现象叫做忙等busy-waiting。
3.8. 轻量锁与偏向锁的区别和不同
争夺轻量级锁失败时线程自旋尝试抢占锁
轻量级锁每次退出同步块都需要释放锁而偏向锁是在竞争发生时才释放锁
4. 重量级锁竞争激烈
4.1. 前言
有大量的线程参与锁的竞争冲突性很高某个达到最大自旋次数的线程会将轻量级锁升级为重量级锁。
当后续线程尝试获取锁时发现被占用的锁是重量级锁则直接进入阻塞状态而不是忙等等待被唤
醒。 因为自旋会消耗CPU为了避免无用的自旋比如获得锁的线程被阻塞住了一旦锁升级成重量级锁就不
会再恢复到轻量级锁状态。
当锁处于这个状态下其他线程试图获取锁时都会被阻塞住当持有锁的线程释放锁之后会唤醒这些线程
被唤醒的线程就会进行新一轮的夺锁之争。
4.2. 自旋优化TODO 能发生偏向锁的也可以发生轻量级锁能发生轻量级锁的也可以适用重量级锁。
当然重量级锁发生在能够获取到锁的情况当不能获取到锁时有时会触发自旋锁。
锁消除和锁粗化也是发生在获取到锁的情况而且是在同步快执行的过程中所以是在偏向锁的里面。
重量级锁竞争的时候还可以使用自旋来进行优化就是先自旋几次暂时不进入阻塞如果当前线程自旋成
功即这时候持锁线程已经退出了同步块释放了锁这时当前线程就可以避免阻塞synchronized是非公
平锁.
当线程在进入尾部队列之前,会尝试着先自旋获取锁,如果获取失败才选择进入尾部队列。阻塞会发生上下文切
换自旋优化尽量避免上下文切换 自旋会占用 CPU 时间单核 CPU 自旋就是浪费多核 CPU 自旋才能发挥优势。
在 Java 6 之后自旋锁是自适应的比如对象刚刚的一次自旋操作成功过那么认为这次自旋成功的可能性会
高就多自旋几次
反之就少自旋甚至不自旋总之比较智能。
Java 7 之后不能控制是否开启自旋功能。
5. 各种锁优缺点、synchronized锁升级和实现原理
注意锁可以升级不可以降级但是偏向锁状态可以被重置为无锁状态 synchronized锁升级过程总结一句话就是先自旋不行再阻塞。
实际上是把之前的悲观锁(重量级锁)变成在一定条件下使用偏向锁以及使用轻量级(自旋锁CAS)的形式
synchronized在修饰方法和代码块在字节码上实现方式有很大差异但是内部实现还是基于对象头的
MarkWord来实现的。
JDK1.6之前synchronized使用的是重量级锁JDK1.6之后进行了优化拥有了无锁-偏向锁-轻量级锁-重
量级锁的升级过程而不是无论什么情况都使用重量级锁。
偏向锁适用于单线程适用的情况在不存在锁竞争的时候进入同步方法/代码块则使用偏向锁。
轻量级锁适用于竞争较不激烈的情况(这和乐观锁的使用范围类似) 存在竞争时升级为轻量级锁轻量级锁
采用的是自旋锁如果同步
方法/代码块执行时间很短的话采用轻量级锁虽然会占用cpu资源但是相对比使用重量级锁还是更高效。
重量级锁适用于竞争激烈的情况如果同步方法/代码块执行时间很长那么使用轻量级锁自旋带来的性能
消耗就比使用重量级锁更严重这时候就需要升级为重量级锁。 五、JIT编译器对锁的优化
Just In Time Compiler一般翻译为即时编译器
1. 锁粗化
按理来说同步块的作用范围应该尽可能小但是加锁解锁也需要消耗资源如果存在一系列的连续加锁解锁
操作可能会导致不必要的性能损耗。锁粗化就是将多个连续的加锁、解锁操作连接在一起扩展成一个范围
更大的锁避免频繁的加锁解锁操作。 例子1
JVM会检测到这样一连串的操作都对同一个对象加锁(while循环内100次执行append,没有锁粗话就要进行
100次加锁/解锁) ,此时JVM就会将加锁的范围粗化到这一连串的操作的外部(比如while循环体外) ,使得这一连
串操作只需要加一次锁即可。
而且JIT编译器在编译期对StringBuffer对象进行逃逸分析如果没有发生逃逸则会使用栈上分配分配完成
后继续在调用栈内执行线程结束后栈空间被回收局部变量对象也被回收这样就不用进行垃圾回收
了。可以减少垃圾回收时间和次数。 例子2
/*** description: 锁粗化* 假如方法中首尾相接前后相邻的都是同一个锁对象那JIT编译器就会把这几个synchronized块合并成一个大块* 加粗加大范围一次申请锁使用即可避免次次的申请和释放锁提升了性能*/
public class LockBigDemo {static Object objectLock new Object();public static void main(String[] args) {new Thread(() - {synchronized (objectLock) {System.out.println(11111);}synchronized (objectLock) {System.out.println(22222);}synchronized (objectLock) {System.out.println(33333);}}, a).start();new Thread(() - {synchronized (objectLock) {System.out.println(44444);}synchronized (objectLock) {System.out.println(55555);}synchronized (objectLock) {System.out.println(66666);}}, b).start();}
}
2. 锁消除
Java虚拟机通过对运行上下文的扫描经过逃逸分析去除不可能存在竞争的锁通过这种方式消除没有必要
的锁可以节省毫无意义的请求锁时间 例子1
StringBuffer是线程安全的 因为它的关键方法都被synchronized修饰过的但是我们看上面代码我们会
发现sb这个引用只会在add方法中使用不可能被其它线程引用(因为是局部变量栈私有)因此sb是不可
能共享的资源JVM会自动消除StringBuffer对象内部的锁 例子2
/*** description: 锁消除* 从JIT角度看相当于无视它synchronized (o)不存在了,这个锁对象并没有被共用扩散到其它线程使用* 极端的说就是根本没有加这个锁对象的底层机器码消除了锁的使用*/
public class LockClearUPDemo {static Object objectLock new Object();//正常的public void m1() {//锁消除,JIT会无视它synchronized(对象锁)不存在了。不正常的//每个线程调用m1方法都会创建一个新的object 即使用的不是同一把锁不存在锁竞争Object o new Object();synchronized (o) {System.out.println(-----hello LockClearUPDemo \t o.hashCode() \t objectLock.hashCode());}}public static void main(String[] args) {LockClearUPDemo demo new LockClearUPDemo();for (int i 1; i 10; i) {new Thread(() - {demo.m1();}, String.valueOf(i)).start();}}
}
实质是JIT编译器的同步省略或者叫同步消除
六、锁降级
一般说是不存在锁降级的
七、扩展
1. 自旋锁持有锁的时间较短
指当一个线程在获取锁的时候如果锁已经被其它线程获取那么该线程将循环等待然后不断的判断锁是否
能够被成功获取直到获取到锁才会退出循环。
2. 自适应自旋锁
自适应自旋解决的是“锁竞争时间不确定”的问题可以根据上一次自旋的时间与结果调整下一次自旋的时间
八、演示示例锁优化的思路和方法
锁优化的思路和方法有以下几种
减少锁持有时间减小锁粒度锁分离锁粗化锁消除
1. 减少锁持有时间
public synchronized void syncMethod(){ othercode1(); mutextMethod(); othercode2();
}
像上述代码这样在进入方法前就要得到锁其他线程就要在外面等待。
这里优化的一点在于要减少其他线程等待的时间所以只需要在有线程安全要求的程序代码上加锁。
public void syncMethod(){ othercode1(); synchronized(this){mutextMethod(); }othercode2();
}
2. 减小锁粒度
将大对象这个对象可能会被很多线程访问拆成小对象大大增加并行度降低锁竞争。降低了锁的竞
争偏向锁轻量级锁成功率才会提高。
最最典型的减小锁粒度的案例就是ConcurrentHashMap。
3. 锁分离
最常见的锁分离就是读写锁ReadWriteLock根据功能进行分离成读锁和写锁这样读读不互斥读写互斥
写写互斥。即保证了线程安全又提高了性能。
读写分离思想可以延伸只要操作互不影响锁就可以分离。
比如LinkedBlockingQueue
从头部取出数据从尾部放入数据使用两把锁。
4. 锁粗化
通常情况下为了保证多线程间的有效并发会要求每个线程持有锁的时间尽量短即在使用完公共资源后
应该立即释放锁。
只有这样等待在这个锁上的其他线程才能尽早的获得资源执行任务。
但是凡事都有一个度如果对同一个锁不停的进行请求、同步和释放其本身也会消耗系统宝贵的资源反
而不利于性能的优化 。
举个例子 public void demoMethod(){ synchronized(lock){ //do sth. } //...做其他不需要的同步的工作但能很快执行完毕 synchronized(lock){ //do sth. } }
这种情况根据锁粗化的思想应该合并 public void demoMethod(){ //整合成一次锁请求 synchronized(lock){ //do sth. //...做其他不需要的同步的工作但能很快执行完毕 }}
当然这是有前提的前提就是中间的那些不需要同步的工作是很快执行完成的。
再举一个极端的例子 for(int i 0; i CIRCLE; i){ synchronized(lock){ //...} }
在一个循环内不同得获得锁。虽然JDK内部会对这个代码做些优化但是还不如直接写成 synchronized(lock){ for(int i0;iCIRCLE;i){ } }
当然如果有需求说这样的循环太久需要给其他线程不要等待太久那只能写成上面那种。
如果没有这样类似的需求还是直接写成下面那种比较好。
5. 锁消除
锁消除是在编译器级别的事情。
在即时编译器时如果发现不可能被共享的对象则可以消除这些对象的锁操作。
也许你会觉得奇怪既然有些对象不可能被多线程访问那为什么要加锁呢写代码时直接不加锁不就好了。
但是有时这些锁并不是程序员所写的有的是JDK实现中就有锁的比如Vector和StringBuffer这样的类
它们中的很多方法都是有锁的。当我们在一些不会有线程安全的情况下使用这些类的方法时达到某些条件
时编译器会将锁消除来提高性能。
比如 public static void main(String args[]) throws InterruptedException {long start System.currentTimeMillis();for (int i 0; i 2000000; i) {createStringBuffer(JVM, Diagnosis);}long bufferCost System.currentTimeMillis() - start;System.out.println(craeteStringBuffer: bufferCost ms);}public static String createStringBuffer(String s1, String s2) {StringBuffer sb new StringBuffer();sb.append(s1);sb.append(s2);return sb.toString();}
上述代码中的StringBuffer.append是一个同步操作但是StringBuffer却是一个局部变量并且方法也并没
有把StringBuffer返回所以不可能会有多线程去访问它。
那么此时StringBuffer中的同步操作就是没有意义的。
开启锁消除是在JVM参数上设置的当然需要在server模式下
-server -XX:DoEscapeAnalysis -XX:EliminateLocks
并且要开启逃逸分析。 逃逸分析的作用呢就是看看变量是否有可能逃出作用域的范围。
比如上述的StringBuffer上述代码中craeteStringBuffer的返回是一个String所以这个局部变量
StringBuffer在其他地方都不会被使用。如果将craeteStringBuffer改成 public static StringBuffer craeteStringBuffer(String s1, String s2) {StringBuffer sb new StringBuffer();sb.append(s1);sb.append(s2);return sb;}
那么这个 StringBuffer被返回后是有可能被任何其他地方所使用的
譬如被主函数将返回结果put进map啊等等。
那么JVM的逃逸分析可以分析出这个局部变量 StringBuffer逃出了它的作用域。 所以基于逃逸分析JVM可以判断如果这个局部变量StringBuffer并没有逃出它的作用域那么可以确定这
个StringBuffer并不会被多线程所访问那么就可以把这些多余的锁给去掉来提高性能。
当JVM参数为
-server -XX:DoEscapeAnalysis -XX:EliminateLocks
输出
craeteStringBuffer: 302 ms
JVM参数为
-server -XX:DoEscapeAnalysis -XX:-EliminateLocks
输出
craeteStringBuffer: 660 ms
显然锁消除的效果还是很明显的。