一、volatile 与 synchronized 关键字 的原理

 

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

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

变量和线程的角度分别阐释)。

禁止JVM指令重排序优化。

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

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

 

二、synchronized关键字的优化

 

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

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

Java 虚拟机对 synchronized 锁的优化_同步锁

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

无锁状态 、 偏向锁状态 、 轻量级锁状态 、 重量级锁状态

再次请求锁的时候,无需在做任何同步操作。这样就节省了大量有关锁申请的操作,从而提高了程序的性能。

撤销锁将会带来额外的性能消耗,因此还不如不启用偏向锁。使用Java虚拟机参数-XX:+UseBiasedLocking可以开启偏向锁。

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

空循环可以获取到锁则进入临界区,如果还是获取不到则系统会真正的挂起线程。

直接进入BLOCKED状态,再次启动去争取同步锁较为消耗时间。

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

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

Java 虚拟机对 synchronized 锁的优化_同步锁_02

 

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

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

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

把锁记录的数据,重写写入锁对象的对象头中,相当于归还一样。如果CAS成功则流程结束,CAS失败执行步骤4)

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

 

 

 

 

 

 

 

 

 

.