youyichannel

志于道,据于德,依于仁,游于艺!

0%

Synchronized全解读-06

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/ 强推 河北王校长