Synchronized 升级到重量级锁之后就下不来了?你错了!

简介: Synchronized 升级到重量级锁之后就下不来了?你错了!

你好,我是 yes。

之前的一篇关于 Synchronized 的一个点,网上99%的文章都错了发了之后有很多读者私聊我,感觉打破了对轻量级锁升级的一个认知。

然后有一位读者的留言倒是提醒到我还有一个点,关于 Synchronized 的这个点,可能很多人的认识也是错误的。

其实以前我的认识也是错误的,直至去年和另一位号主安琪拉讨论的时候才意识到这个问题。

我们都知道 synchronized 的锁升级,也听过锁升级之后不会降级,所以理所当然的认为当一个锁升级为重量级锁之后,任何线程再来争抢之后会走重量级锁的逻辑。

不会再从无锁到偏向锁到轻量级锁再到重量级锁。

那事实是怎样的呢?这篇咱们不看源码,直接整结果。

我们直接看锁对象的锁标志来判断。


image.png


image.png


代码非常简单,先看下无锁的对象布局,然后多线程争抢此时应该是重量级锁,然后 sleep 等待所有线程执行完毕释放锁,然后再看看此时的锁布局。

最后再加一次锁看看对象布局。

这里有个注意点

1.8 的偏向锁是会延迟生效的,得在 JVM 启动 4 秒后生效,通过 -XX:BiasedLockingStartupDelay=0关闭偏向锁延迟

这边我没搞这个参数,因为不是重点,所以等下结果里面不会有偏向锁。

上结果!




结果显而易见,初始是无锁的。

然后4个线程同时竞争变成了重量级锁。

4个线程执行完毕之后,锁对象变成了无锁。

此时再有一个线程去争抢锁,就从无锁变成了轻量级锁。

所以当重量级锁释放了之后,锁对象是无锁的!

有新的线程来竞争的话又会从轻量级锁开始!

好了,over。

现在再看下 openjdk wiki 上面的这个图,关注右边解锁方面


image.png



是不是印证了?


最后


想要自己测试的话,引用一个 jol 的包即可

<dependency>  
   <groupId>org.openjdk.jol</groupId>  
   <artifactId>jol-core</artifactId>  
   <version>0.14</version>  
  </dependency>  

代码也直接拷给你们:

public class YesLockTest {  
    static Object yesLock;  
    public static void main(String[] args) throws InterruptedException {  
        yesLock = new Object();  
        System.out.println("无锁时对象布局:" + ClassLayout.parseInstance(yesLock).toPrintable());  
        IntStream.rangeClosed(1,4).forEach(i->{getYesLock();});  
        Thread.sleep(5000L);  
        System.out.println("无竞争之后,此时的对象布局:" + ClassLayout.parseInstance(yesLock).toPrintable());  
        getYesLock();//此时再来一次加锁  
    }  
    private static void getYesLock() {  
        new Thread(() -> {  
            try {  
                synchronized (yesLock) {  
                    System.out.println("线程[" + Thread.currentThread().getName() + "]" +  
                            ":重量级锁状态对象布局:" + ClassLayout.parseInstance(yesLock).toPrintable());  
                }  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }).start();  
    }  
}  


相关文章
|
6月前
|
存储 监控 安全
解锁ThreadLocal的问题集:如何规避多线程中的坑
解锁ThreadLocal的问题集:如何规避多线程中的坑
293 0
|
3月前
|
存储 Java C++
人人都会的synchronized锁升级,你真正从源码层面理解过吗?
本文结合Jvm源码层面分析synchronized锁升级原理,可以帮助读者从本质上理解锁升级过程,从锁如何存储,存储在哪的疑问出发,一步一步彻底剖析偏向锁,轻量级锁,自旋锁,重量级锁的原理和机制。
人人都会的synchronized锁升级,你真正从源码层面理解过吗?
|
3月前
|
Java 开发者
解锁Java并发编程的秘密武器!揭秘AQS,让你的代码从此告别‘锁’事烦恼,多线程同步不再是梦!
【8月更文挑战第25天】AbstractQueuedSynchronizer(AQS)是Java并发包中的核心组件,作为多种同步工具类(如ReentrantLock和CountDownLatch等)的基础。AQS通过维护一个表示同步状态的`state`变量和一个FIFO线程等待队列,提供了一种高效灵活的同步机制。它支持独占式和共享式两种资源访问模式。内部使用CLH锁队列管理等待线程,当线程尝试获取已持有的锁时,会被放入队列并阻塞,直至锁被释放。AQS的巧妙设计极大地丰富了Java并发编程的能力。
44 0
|
5月前
|
Java 程序员
【面试官】知道synchronized锁升级吗
线程A获取了某个对象锁,但在线程代码的流程中仍需再次获取该对象锁,此时线程A可以继续执行不需要重新再获取该对象锁。既然获取锁的粒度是线程,意味着线程自己是可以获取自己的内部锁的,而如果获取锁的粒度是调用则每次经过同步代码块都需要重新获取锁。此时synchronized重量级锁就回归到了悲观锁的状态,其他获取不到锁的都会进入阻塞状态。来获得锁,CAS操作不需要获得锁、释放锁,减少了像synchronized重量级锁带来的。轻量级锁通过CAS自旋来获得锁,如果自旋10次失败,为了减少CPU的消耗则锁会膨胀为。
160 4
|
5月前
|
安全 Java 编译器
synchronized同步锁 : 原理到锁升级及历史演进的解析
synchronized同步锁 : 原理到锁升级及历史演进的解析
|
安全 算法 Java
可重入锁,不可重入锁,死锁的多种情况,以及产生的原因,如何解决,synchronized采用的锁策略(渣女圣经)自适应的底层,锁清除,锁粗化,CAS的部分应用
可重入锁,不可重入锁,死锁的多种情况,以及产生的原因,如何解决,synchronized采用的锁策略(渣女圣经)自适应的底层,锁清除,锁粗化,CAS的部分应用
|
Java 开发者
解锁Java多线程编程中的死锁之谜
解锁Java多线程编程中的死锁之谜
57 0
|
6月前
|
Java 调度
没了解死锁怎么能行?进来看看,一文带你拿下死锁产生的原因、死锁的解决方案。
没了解死锁怎么能行?进来看看,一文带你拿下死锁产生的原因、死锁的解决方案。
50 0
|
存储 Java
面试~Synchronized 与 锁升级
面试~Synchronized 与 锁升级
59 0
|
存储 安全 Java
08.从源码揭秘偏向锁的升级
大家好,我是王有志。上一篇学习了synchronized的用法,今天我们深到synchronized的原理,来学习偏向锁升级到轻量级锁的过程。
170 0
08.从源码揭秘偏向锁的升级