推荐链接:
总结——》【Java】
总结——》【Mysql】
总结——》【Redis】
总结——》【Kafka】
总结——》【Spring】
总结——》【SpringBoot】
总结——》【MyBatis、MyBatis-Plus】
总结——》【Linux】
总结——》【MongoDB】
总结——》【Elasticsearch】
Java——》synchronized锁升级
- 一、无锁
- 1、无锁
- 2、匿名偏向
- 二、偏向锁
- 1、获取锁资源的过程(锁升级过程)
- 2、为什么要有偏向锁延迟
- 3、为什么偏向锁撤销会导致启动变慢
- 4、偏向锁撤销安全点
- 5、偏向锁重入
- 6、偏向锁会降级到无锁状态吗以及如何降
- 三、轻量级锁
- 四、重量级锁
- 1、重量级锁会降级到偏向锁或者是轻量级锁吗
- 2、如何竞争锁资源
synchronized在jdk1.6之前,一直是重量级锁,只要线程获取锁资源失败,直接挂起线程(用户态切换到内核态),效率低,所以JDK团队在Jdk1.6将synchronized做了3方面优化:锁升级、锁消除、锁膨胀。
锁就是对象,Java中所有对象都是锁。
锁 | 描述 |
---|---|
无锁/匿名偏向 | 无锁:没有开启偏向锁(偏向锁延迟开启时间内),没有线程拿锁 匿名偏向:开启偏向锁,没有线程拿锁,没有偏向任何线程 |
偏向锁 | 只有一个线程来拿锁资源,没有竞争 |
轻量级锁 | 偏向锁出现竞争时,会升级到轻量级锁(触发偏向锁撤销),以CAS的方式(自适应自旋锁)去竞争锁资源,不会让线程挂起。(LockRecord) |
重量级锁 | 直接采用MarkWord指向的ObjectMonitor以传统的方式去竞争锁资源 |
一、无锁
1、无锁
一般情况下,new出来的一个对象,是无锁状态。
因为偏向锁有延迟,在启动JVM的4s中,不存在偏向锁。
2、匿名偏向
如果关闭了偏向锁延迟的设置,new出来的对象,就是匿名偏向,但没有偏向任何线程。
//关闭延迟开启偏向锁
-XX:BiasedLockingStartupDelay=0
//4s之后开启偏向锁
-XX:BiasedLockingStartupDelay=4
//禁止偏向锁
-XX:-UseBiasedLocking
//启用偏向锁
-XX:+UseBiasedLocking
二、偏向锁
没有线程的竞争,只有一个线程在获取锁资源。
1、获取锁资源的过程(锁升级过程)
当某一个线程来获取这个锁资源时,发现没有线程占用锁资源,并且锁是偏向锁,使用CAS的方式,设置对象的线程ID为当前线程,获取到锁资源,下次当前线程再次获取锁资源时,只需要判断是偏向锁,并且对象的线程ID是当前线程ID就直接获得到锁资源。如果对象的线程ID不是当前线程ID,也就是说偏向锁状态出现了锁竞争的情况,就触发锁升级,升级为轻量级锁。
2、为什么要有偏向锁延迟
JVM在启动时,需要加载大量的.class文件到内存中,这个操作会涉及到synchronized的使用,为了避免出现偏向锁撤销导致启动效率变慢,所以JVM启动时,有一个延迟4s开启偏向锁的操作。
偏向锁是延迟开启的,并且在开启偏向锁之后,默认不存在无锁状态,只存在匿名偏向。
3、为什么偏向锁撤销会导致启动变慢
当偏向锁升级到轻量锁时,会触发偏向锁撤销。
(1)偏向锁撤销需要等到一个安全点(STW),才可以做偏向锁撤销。
(2)偏向锁撤销的成本太高(消耗资源)
4、偏向锁撤销安全点
- GC
- 方法返回之前
- 调用某个方法之后
- 抛出异常的位置
- 循环的末尾
5、偏向锁重入
用到了LockRecord,只不过内部不会存储hashcode信息等等,在偏向锁重入时,每次都会压栈一个LockRecord,从而实现偏向锁重入。
6、偏向锁会降级到无锁状态吗以及如何降
会。
当偏向锁状态下,获取当前对象的hashcode值,会因为对象头空间无法存储hashcode,导致降级到无锁状态。
三、轻量级锁
如果出现了多个线程的竞争,就要升级为轻量级锁(有可能直接从无锁变为轻量级锁,也有可能从偏向锁升级为轻量级锁,会触发偏向锁撤销)。
轻量级锁的是基于CAS尝试获取锁资源,这里会用到自适应自旋锁,JVM会自动的根据上次CAS成功与否,决定这次自旋多少次。
- 如果成功获取到,拿着锁资源走
- 如果自旋了一定次数,没拿到锁资源,锁升级为重量级锁
四、重量级锁
轻量级锁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){
//t1 - 偏向锁
System.out.println("t1:" + ClassLayout.parseInstance(o).toPrintable());
}
}).start();
//main - 偏向锁 - 轻量级锁CAS - 重量级锁
synchronized (o){
System.out.println("main:" + ClassLayout.parseInstance(o).toPrintable());
}
}
1、重量级锁会降级到偏向锁或者是轻量级锁吗
不会
2、如何竞争锁资源
直接采用MarkWord指向的ObjectMonitor以传统的方式去竞争锁资源