youyichannel

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

0%

Synchronized全解读-05

Synchronized锁升级 —— 偏向锁的撤销

偏向锁状态变化与最终升级为轻量级锁:

  1. A线程获取偏向锁成功,已经退出执行不再是活跃线程;B线程来获取偏向锁,默认前20次直接升级为轻量级锁
  2. A线程获取偏向锁成功,已经退出执行不再是活跃线程;B线程来获取偏向锁,默认20次以后,对象锁偏向线程B。达到40次阈值后,若再有其他线程(比如线程C)过来争抢,则触发批量撤销,该对象不再有任何偏向锁的情况。

批量重偏向:当一个对象,如果在短时间内(默认25s)在经过默认20次争抢的情况下,会将后面的所有争抢重新偏向争抢者线程。

代码示例:

public class SyncLockFlag1 {

static Thread A;
static Thread B;
static int loopFlag = 20;

public static void main(String[] args) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

final List<Object> list = new ArrayList<>();
A = new Thread(() -> {
for (int i = 0; i < loopFlag; i++) {
Object obj = new Object();
list.add(obj);
System.out.println("A加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
synchronized (obj) {
// 线程A持有的都是偏向锁
System.out.println("A加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}
System.out.println("A加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}

LockSupport.unpark(B);
}, "Thread-A");

B = new Thread(() -> {
LockSupport.park();
for (int i = 0; i < loopFlag; i++) {
Object obj = list.get(i);
System.out.println("B加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
synchronized (obj) {
System.out.println("B加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}
System.out.println("B加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}

System.out.println("新产生的对象:" + ClassLayout.parseInstance(new Object()).toPrintable());
}, "Thread-B");

A.start();
B.start();
}
}

当B线程争抢第16次(不同的机器不同)的时候,触发了批量重偏向的阈值,在第16次及之后的争抢中,JVM会将对象的锁偏向线程B,因为JVM认为这个对象更适合线程B。

批量撤销

代码示例

public class SyncLockFlag2 {

static Thread A;
static Thread B;
static Thread C;
static int loopFlag = 40;

public static void main(String[] args) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

final List<Object> list = new ArrayList<>();
A = new Thread(() -> {
for (int i = 0; i < loopFlag; i++) {
Object obj = new Object();
list.add(obj);
System.out.println("A加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
synchronized (obj) {
// 线程A持有的都是偏向锁
System.out.println("A加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}
System.out.println("A加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}

LockSupport.unpark(B);
}, "Thread-A");

B = new Thread(() -> {
LockSupport.park();
for (int i = 0; i < loopFlag; i++) {
Object obj = list.get(i);
System.out.println("B加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
synchronized (obj) {
System.out.println("B加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}
System.out.println("B加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}
LockSupport.unpark(C);

System.out.println("B 新产生的对象:" + ClassLayout.parseInstance(new Object()).toPrintable());
}, "Thread-B");

C = new Thread(() -> {
LockSupport.park();
for (int i = 0; i < loopFlag; i++) {
Object obj = list.get(i);
System.out.println("C加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
// 偏向撤销次数已达阈值40,执行批量撤销
synchronized (obj) {
System.out.println("C加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}
System.out.println("C加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable());
}
// 新产生的对象不能偏向
System.out.println("C 新产生的对象:" + ClassLayout.parseInstance(new Object()).toPrintable());
}, "Thread-C");

A.start();
B.start();
C.start();
}
}

如果基于批量重偏向的基础上,还在继续争抢达到40次,并且有第三条线程C加入争抢,此时会触发批量撤销。JVM会标记改对象不能使用偏向锁,以后新创建的示例对象直接以轻量级锁开始,此时才是真正完成了锁升级。

真正的锁升级,是依赖于对象的Class字节码的,而不是依赖于该对象Class的某一个实例对象。

资料:https://www.bilibili.com/video/BV1Je411f7PF/ 强推 河北王校长