synchronized锁升级分析

    技术2024-12-30  21

    synchronized锁升级分析

    1、Mutex介绍

    Mutex中文名称是互斥锁,跟着中文名称很好理解了,就是为了互斥; 在并发的情况下,对于一个对象的操作,可能会导致数据不一致性问题,为了保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象;

    2、经典问题引入

    前言:synchronized是获取锁对象!!!是一个操作,将锁对象加到代码上面;(这句话多记几遍,不然下面会踩坑) 下面这段代码,创建了1000条线程去执行一个static的变量,每一条线程使的count加一,为了使问题更加明显,这里让线程在count++之前,睡眠了一毫秒。在打印输出结果之前,再让主线程睡眠三秒,保证所有线程都执行完再打印数据;原本预期结果是1000,按照正常逻辑也应该是1000.其实不然,每个电脑的结果不同,你自己试着跑一遍吧。

    打印出来肯定不是1000,根据JMM的模型,可以推断是工作内存的变量没有同步到主内存当中。

    采用synchronized给当前对象加锁,打印结果是1000

    通过字节码分析,可以看到同步的部分是使用monitorenter和monitorexit指令。这两个指令隐式地执行了mutex的lock和unlock操作,用于提供原子性的操作;

    这两个指令的命名也有有原因的,原则上他们是获取了这个对象的监视器(monitor),这个过程是排他的,也就是说同一时刻只有一个对象能获取到由synthronized保护的对象;

    好了,我们加上synchronized修饰,运行结果是1000毫无疑问的

    3、深入了解synchronized

    首先明确一下我们只需要关注对象头的此处位置三个数字,这个地方是对象的锁状态(下面这张图只是举例说明看哪);

    然后我们只需关于这些状态

    Java SE1.6的时候对于synchronized进行了优化,也就是synchronized在加锁的时候,里面关于锁的机制进行升级,升级的过程如下流程图:

    (以下都是加了synchronized的时候进行分析的结果)

    当线程第一次访问这个对象时:

    解释:线程一访问到synchronized代码块的时候,先检查对象的标记位,第一次进来的线程肯定不会读到标记检查,就将对象占有,进行标记,然后执行代码块,执行完成之后不会清除对象的标记。(这个过程是偏向锁的过程)

    当有第二个线程接着访问此对象的时候:

    解释:当第二个线程访问到synchronized代码块的时候,检查到有标记(因为上一个对象不会清除),判断上一个对象是否存活,如果不存活了,则跟第一个线程进来一样的步骤,如果存活,则进入到轻量级锁,也就是锁自旋;如果自旋太久了了,也就是大于十次了,就转移到重量级锁,将线程挂起;

    4、synchronized锁验证

    无锁状态:

    偏向锁

    解释:因为JVM底层进行加载的时候,会将延时加载的对象加上偏向锁; 注意:此时的偏向锁是一种特殊的偏向锁,具体往下看

    在对象头的位置上,我们可以看到线程ID此时其实是全为零的,再结合偏向锁的概念,偏向锁,偏向、偏向…其实就是偏向了某个线程,此时不偏向其他线程,也可以理解这个就是一个特殊的“无锁”;

    那么怎么让他偏向呢?

    上图的代码可以让这把锁进行偏向

    可以看到依然是偏向锁的情况下,有了偏向的线程ID了 注意:你要讲访问的对象加synchronized修饰才有这种效果

    自旋锁(轻量级锁):

    自旋锁是自我上锁了,这个自我上锁的条件,是上一个线程还存活,那么就想办法让线程存活的情况下,再执行一条线程;

    在主线程不在睡眠直接加载类的情况下,JVM不底层没有触碰到stu的synchronized代码片段,但是在创建第二个线程创建的时候,JVM底层触碰到了stu的synchronized代码片段,导致了中间会变化了偏向锁,然后根据流程图,第二个线程访问这个具有偏向锁,且第一个线程为消亡的情况下(在main线程当中创建了另外一个线程,main线程肯定还不会消亡),会将偏向锁改为轻量级锁(自旋锁)。

    ps:JVM中间转换过程目前能力原因找不到方法去证明,这是一个推断的想法。如果你有方法证明,可以联系一下我吗?

    重量级锁:

    自旋状态还没结束,就会导致不断去自旋,自旋次数超过十了,就会将锁机制转换到重量级锁,这个过程可能比较麻烦去验证,这里主要是证明锁的存在,以及验证什么情况会产生不同的锁。

    【小结】

    synchronized在这此笔记当中只是简单了验证了锁升级,以及synthronized的简单了解。关于synthronized还有很多很多需要学习的,锁的加锁过程怎么去debug,发现升级过程这些的,还需要好好加强

    【题外话】

    因为有点好奇延迟加载究竟延迟加载多久才加锁,在我自己电脑进行了测试,在写文章的时候是睡眠了这个值,也就是在我自己电脑上,睡眠了这么长的时间情况下,多运行几次会产生在无锁与偏向锁的切换,有兴趣的小伙伴可以自行尝试,然后在下方留言(这个对我有帮助哦,谢谢你啦)

    Processed: 0.009, SQL: 9