Synchronized锁升级 ——
轻量级锁加锁与解锁
1)轻量级锁加锁
线程在执行同步代码块之前,JVM会先在当前线程的栈帧中创建存储锁记录的空间(Lock
Record 记录),并将对象头中的Mark
Word(前30位:25位的hashcode,4位的分代年龄,1位是否是偏向锁标志)复制到锁记录中,官方称之为Displaced Mark Word
。然后线程尝试使用CAS将对象头中的Mark
Word替换为指向锁记录的指针(指向线程栈帧中Lock
Record的指针),如果成功,当前线程获得锁;如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋 来获取锁。
2)轻量级锁解锁
轻量级锁解锁时,会使用原子的CAS操作将Displaced Mark Word
(Lock
Record
记录)替换到对象头中,如果成功,则表示没有竞争发生;如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。
示例代码:
需要关闭偏向锁,配置-XX:BiasedLockingStartupDelay=100000000
public class LightLock { public static void main (String[] args) throws InterruptedException { Object obj = new Object (); System.out.println("A 加锁前:" + ClassLayout.parseInstance(obj).toPrintable()); Thread A = new Thread (() -> { synchronized (obj) { System.out.println("A 加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); try { Thread.sleep(2000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } } }); A.start(); Thread.sleep(500 ); System.out.println("B 加锁前:" + ClassLayout.parseInstance(obj).toPrintable()); Thread B = new Thread (() -> { synchronized (obj) { System.out.println("B 加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } } }); B.start(); Thread.sleep(5000 ); synchronized (obj) { System.out.println("再次加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); } Object newObj = new Object (); synchronized (newObj) { System.out.println("新对象加锁中:" + ClassLayout.parseInstance(newObj).toPrintable()); } } }
测试结果:
结论:
锁升级,是依赖于class的,而并不是依赖于某一个创建的对象实例(偏向锁升级为轻量级锁)
锁升级,是依赖于当前创建的对象实例的(轻量级锁升级为重量级锁)
轻量级锁升级为重量级锁,只要线程发生了竞争,并且CAS替换失败,就会触发锁膨胀,升级为重量级锁,针对实例对象,并非对象的class。
资料:https://www.bilibili.com/video/BV1rQ4y1b73T/ 强推 河北王校长