JVM的锁优化-锁升级
锁升级
锁升级,是JDK1.6
版本中对于synchronized
的优化。调查发现一般情况下锁的使用都是为了处理一些极端情况,但多时间,并不会出现并发争强的情况,直接是有synchronized
比较重,会影响系统性能。
升级步骤: 无锁/匿名偏向锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
升级特点:一旦升级,无法降级
锁状态对照表:(根据下图,可查对锁状态)
无锁
所有对象最初都是无锁状态。
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
偏向锁与轻量级锁
轻量级锁
当第一线程获取到锁后,锁状态由无锁转为偏向锁
。但实际发现锁状态是轻量级锁似乎没有偏向锁
。~?!
这其中涉及到jvm的偏向锁开启机制,5秒开始。5秒内,锁状态会由无锁转为轻量级锁。
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
// 获取锁
// 此时锁,却是轻量级锁
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
此时是轻量级锁
偏向锁
添加5秒睡眠后,会出现偏向锁。此时会发现,偏向锁分为两钟,一个普通偏向锁,一个是匿名偏向锁。
需要注意的是,当jvm开启偏向锁后,所有对象锁状态会自动转为匿名偏向锁
,只会变更锁状态,不会记录线程信息。
public static void main(String[] args) throws InterruptedException {
// 5秒睡眠
Thread.sleep(6000);
Object o = new Object();
// 此时锁为 匿名偏向级锁。即便是没有使用synchronized
System.out.println(ClassLayout.parseInstance(o).toPrintable());
// 此时锁为 偏向级锁
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
重量级锁
当轻量级锁进行CAS,当自旋达到一定次数后,锁升级为重量级锁。
public static void main(String[] args) throws InterruptedException {
Thread.sleep(5000);
Object o = new Object();
// 此时锁是 匿名偏向锁
System.out.println(ClassLayout.parseInstance(o).toPrintable());
new Thread(() -> {
// 线程拿到锁后,锁状态变为偏向级锁
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}).start();
// 尝试获取锁后,发现是锁已经被获取,已是偏向级锁
// 之后升级为 自旋锁,然后cas 一定次数后,升级为重量级锁
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
// 如果cpu好的话,此时输出可能是轻量级锁
}
}
须知
5秒开始偏向锁设定原因 **
偏向锁转化为轻量级锁时,会涉及到偏向锁撤销操作。查看ClassLoader源码发现,但在大量并发加载class的情况下,锁撤销会影响性能。为了避免添加了5秒开启,让锁状态直接从无锁转化为轻量级锁。
锁升级,但是否会降级 **
会,从匿名偏向锁会降级为无锁
匿名偏向锁状态下,如果系统调用hashCode()
方法,需要将匿名偏向锁变为无锁状态,来存储HashCode。如果有线程获取锁,锁状态会直接从无锁转为轻量级锁,不会再升级为偏向锁。
锁升级流程
- 无锁、匿名偏向锁: 当前对象没有作为锁存在
- 偏向锁:如果当前锁资源,只有一个线程在频繁的获取与释放,那么这个线程过来,只需要判断,当前只想的线程是否是当前线程
- 如果是,直接拿走资源
- 如果不是,基于CAS的方式,尝试将偏向锁指向当前线程,如果获取不到,处方锁升级,升级为情况级锁
- 轻量级锁:会采用自旋锁的方式频繁的以CAS的形式获取资源
- 如果成功,拿走资源
- 如果不成功,自旋了一定次数后,如果还不成功,就锁升级
- 重量级锁:就是传统的synchronized方式,拿不到锁资源,就挂起当前线程
对象头信息参考图
Synchronized 是基于对象实现的锁,所以锁信息就放在对象头的MarkWork中。
重量级锁,是否还会CAS中
会
重量级锁是C写的,里面有关于CAS的使用,使用方式与java的相似。