Synchronized与锁升级——轻量级锁
轻量级锁
多线程竞争,但是任意时刻最多只有一个线程竞争,即不存在锁竞争太过激烈的情况,也就没有线程阻塞。
轻量级锁的获取
轻量级锁是为了在线程近乎交替执行同步块时提高性能。
主要目的:在没有多线程竞争的前提下,通过CAS减少重量级锁使用操作系统互斥量产生的性能消耗.说白了先自旋,不行才升级阻塞。
升级时机:当关闭偏向锁功能或多线程竞争偏向锁会导致偏向锁升级为轻量级锁
- 假如线程A己经拿到锁,这时线程B又来抢该对象的锁,由于该对象的锁己经被线程A拿到,当前该锁己是偏向锁了。
- 而线程B在争抢时发现对象头Mark Ward中的线程ID不是线程B自己的线程1D(而是线程A),那线程B就会进行CAS操作希望能获得锁。此吋线程B操作中有两种情况:
如果锁获取成功,直接替换Mark Word中的线程ID为B自己的ID(A—B).重新偏向于其他线程(即将偏向锁交给其他线程,相当于当前线程"被"释放了锁),该锁会保持偏向锁状态,A线程Over,B线程上位: 如果锁获取失败,则偏向锁升级为轻量级锁(设置偏向锁标识为0并设置锁标志位为00),此时轻量级锁由原持有偏向锁的线程持有,继续执行其同步代码,而正在竞争的线程B会进入自旋等待获得该轻量级锁。
CAS自旋达到一定次数会升级为重量级锁
java6之前 自旋锁
java6之后 自适应自旋锁
线程如果自旋成功了,那下次自旋的最大次数会增加,因为JVM认为既然上次成功了,那么这一次也很大概率会成功。
反之
如果很少会自旋成功,那么下次会减少自旋的次数甚至不自旋,避免CPU空转。
总之,自适应意味着自选的次数不是固定不变的,而是根据:
- 同一个锁上一次自旋的时间
- 拥有锁线程的状态来决定。
轻量锁的加锁
JVM会为每个线程在当前线程的栈帧中创建用于存储锁记录的空间(Lock Record),官方成为Displaced Mark Word。
若一个线程获得锁时发现是轻量级锁,会把锁的MarkWord复制到自己的(Lock Record)Displaced Mark Word里面。然后线程尝试用CAS将锁的MarkWord替换为指向锁记录(Lock Record)的指针。如果成功,当前线程获得锁,如果失败,表示Mark Word已经被替换成了其他线程的锁记录,说明在与其它线程竞争锁,当前线程就尝试使用自旋来获取锁。
轻量锁的释放
在释放锁时,当前线程会使用CAS操作将Displaced Mark Word的内容复制回锁的Mark Word里面。(把线程中复制的Displaced Mark Word对象替换当前的Mark Word。)
如果替换成功,整个同步过程就完成了。 如果替换失败,说明有其他线程尝试过获取该锁(此时锁已膨胀),那就要在释放锁的同时,唤醒被挂起的线程。
轻量锁和偏向锁的区别和不同
-
争夺轻量级锁失败时,自旋尝试抢占锁 轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁