不同线程 threadId 一致,导致偏向锁的重新偏向
- 一、出现重复偏向的代码块
- 二、多次运行后出现问题的打印结果
一、出现重复偏向的代码块
- 首先修改 jvm启动参数设置延迟偏向时间 0 [-XX:BiasedLockingStartupDelay=0]
- 可以看到下面的代码创建了三个异步线程,每个线程的任务是打印线程信息及对象的头信息,线程创建之后立即执行并通过join()方法主线程阻塞,直至异步线程执行结束后再创建新线程执行。
- 由于偏向锁延迟时间是0,所以第一次打印对象头信息为偏向锁,并且为可偏向状态;第二次为线程1开始执行,再次打印对象头那么头信息也肯定为偏向锁,并且线程id指向线程1;那么线程2开始执行由于偏向锁线程id已经指向线程1,线程2经过CAS操作,获得到的锁必然是轻量级锁,线程3同理也是轻量级锁。
//修改jvm启动参数设置延迟偏向时间 0 [-XX:BiasedLockingStartupDelay=0]
import static java.lang.System.out;
public static void main(String[] args) throws InterruptedException {
A a = new A();
out.println(ClassLayout.parseInstance(a).toPrintable());
Thread thread1 = new Thread(runnable(a),"thread1");
thread1.start();
thread1.join();
Thread thread2 = new Thread(runnable(a),"thread2");
thread2.start();
thread2.join();
Thread thread3 = new Thread(runnable(a),"thread3");
thread1.start();
}
static Runnable runnable(A a) {
return () -> {
synchronized (a) {
out.println(Thread.currentThread().getId() + ":" + Thread.currentThread().getName());
out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
二、多次运行后出现问题的打印结果
- 我们运行了3次,结果都和上述结论状态变化一致:
偏向、偏向、轻量、轻量
,当我们第四次运行时出现了:偏向、偏向、偏向、偏向
的打印结果。
"C:\Program Files\Java\jdk1.8.0_221\bin\java.exe"
com.usual.header.A object internals:
OFFSET SIZE TYPE DESCRIPTION
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000)(5)
12:thread1
com.usual.header.A object internals:
OFFSET SIZE TYPE DESCRIPTION
0 4 (object header) 05 f0 c7 1f (00000101 11110000 11000111 00011111)(533196805)
13:thread2
com.usual.header.A object internals:
OFFSET SIZE TYPE DESCRIPTION
0 4 (object header) 05 f0 c7 1f (00000101 11110000 11000111 00011111)(533196805)
14:thread3
com.usual.header.A object internals:
OFFSET SIZE TYPE DESCRIPTION
0 4 (object header) 05 f0 c7 1f (00000101 11110000 11000111 00011111)(533196805)
- 根据上述对象头 mark_word 部分打印可知,线程123获得的thread_id是一致的,线程2在线程1运行结束后,由于线程id一致所以无需CAS操作,对象头信息未改变,直接获得偏向锁。
- 分析可知是java线程id复用导致,因为上一个线程 dead,新的线程可以复用上一个线程的thread_id , 我们可以在thread2线程创建前,新建一个线程并启动,就会发先最终打印结果为
偏向、偏向、轻量、轻量
,不再出现重新偏向的现象了。