Java 虚拟机对 synchronized 锁的优化

版权声明:欢迎转载大宇的博客,转载请注明出处: https://blog.csdn.net/yanluandai1985/article/details/82627014

一、volatile 与 synchronized 关键字 的原理

        Java代码首先会被编译成字节码文件。字节码文件被加载到JVM中,JVM将字节码翻译成汇编指令,从而在CPU中执行。

        谈到volatile关键字,就会想到两点:

        (1)让变量在多个线程之间可见,并且强制线程从公共堆栈中取得变量的值(常见的从变量和线程的角度分别阐释)。

        (2)禁止JVM指令重排序优化

         上述只是现象,原理可以粗略理解为:强制线程把修改的数据写回主内存,并且让其它线程的相关缓存失效,需要从主存中重新读取数据。

        synchronized本质其实就是获取对象的监视器,这个过程是排他的,同一时刻只有一个线程能够获取到同步锁对象的监视器
获取对象监视器的线程失败后,将会回到同步队列,其线程的状态进入BLOCKED状态

二、synchronized关键字的优化

        CAS可以粗略的理解为 "读改写" 操作。

        对象的结构简单示意:对象由 “对象头” 和 “对象实例”组成。对象头 由“ 锁状态”等信息组成。

        synchronized 用的锁是存在 Java 对象头里的。

        锁的状态有4种,级别从低到高依次是: 无锁状态偏向锁状态轻量级锁状态 重量级锁状态

        (1)偏向锁:如果一个线程获得了锁,那么锁就会进入偏向模式。当这个线程再次请求锁的时候,无需在做任何同步操作。这样就节省了大量有关锁申请的操作,从而提高了程序的性能。

        因此,对于几乎没有锁竞争的场合,偏向锁有比较好的优化效果,因为连续多次极有可能是同一个线程请求相同的锁。而对于锁竞争比较激烈的场合,其效果不佳。因为在竞争激烈的场合,最有可能的情况是每次都是不同的线程来请求相同的锁。这样偏向模式会失效,撤销锁将会带来额外的性能消耗,因此还不如不启用偏向锁。使用Java虚拟机参数-XX:+UseBiasedLocking可以开启偏向锁。

        (2)轻量级锁:如果锁被其它线程抢夺了,也就是偏向锁失败,虚拟机并不会立即挂起线程。它还会使用一种称为轻量级锁的优化手段。轻量级锁的目标是允许多个线程获取锁,但是不能出竞争, 否则轻量级锁膨胀为重量级锁。轻量级锁只是简单地将对象头部作为指针,指向持有锁的线程堆栈的内部,来判断一个线程是否持有对象锁。如果线程获得轻量级锁成功,则可以顺利进入临界区。如果轻量级锁加锁失败,则表示其他线程抢先争夺到了锁,那么当前线程的锁请求就会膨胀为重量级锁。竞争的线程不会真正的阻塞,它通过不断的自旋来争取同步锁,相当于还没有待机

        (3)自旋锁:轻量级锁就会膨胀为重量级锁后,虚拟机为了避免线程真实的在操作系统层面挂起,虚拟机还会在做最后的努力--自旋锁。若经过几个空循环可以获取到锁则进入临界区,如果还是获取不到则系统会真正的挂起线程。

        (4)重量级锁:线程没有争取到锁,直接进入BLOCKED状态,再次启动去争取同步锁较为消耗时间。

        为什么锁的升级无法逆向?

        自旋锁无法预知到底会空循环几个时钟周期,并且会很消耗 CPU,为了避免这种无用的自旋操作,一旦锁升级为重量锁,就不会再恢复到轻量级锁

è¿éåå¾çæè¿°

        轻量级锁
        如果说偏向锁是只允许一个线程获得锁,那么轻量级锁就是允许多个线程获得锁,但是只允许他们顺序拿锁,不允许出现竞争,也就是拿锁失败的情况,轻量级锁的步骤如下:

        1)线程1在执行同步代码块之前,JVM会先在当前线程的中创建一个锁记录,然后再把同步锁的对象头中的Mark Word复制到该锁记录中,官方称之为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word 指向那个线程的锁记录,即让锁对象记录哪个线程要获取锁。如果成功,则获得锁,进入步骤3)。如果线程发现指针指向了别的线程的锁记录,则就是有其它线程在竞争同步锁,则失败执行步骤2)

        2)线程自旋,自旋成功则获得锁,进入步骤3)。自旋失败,则膨胀成为重量级锁,并把锁标志位变为10,线程阻塞进入步骤3)

        3)锁的持有线程执行同步代码,执行完CAS替换Mark Word成功释放锁,即把锁记录的数据,重写写入锁对象的对象头中,相当于归还一样。如果CAS成功则流程结束,CAS失败执行步骤4)

        4)CAS执行失败说明期间有线程尝试获得锁并自旋失败,轻量级锁升级为了重量级锁,此时释放锁之后,还要唤醒等待的线程

 

.

猜你喜欢

转载自blog.csdn.net/yanluandai1985/article/details/82627014