synchronized在1.6之后发生了很大改动,做了很多优化。因为Doug Lea大师的ReentrantLock类设计的很完美,jdk开发人员表示不服,所以对synchronized做了很大的改动。
此博客针对已对synchronized锁有所了解的。
首先我们需要知道synchronized具有4种锁的状态(细分5种):
- 无锁无偏向
- 无锁可偏向
- 有锁已偏向
- 轻量锁
- 重量锁
在JVM中,偏向锁的启动时会有延迟触发的,因为jvm在启动线程之前会有许多synchronized操作,jvm内部默认这些操作为轻量级操作并非偏向操作。因为对于偏向锁的升级,是十分消耗性能的,需要先撤销再升级。
请看下列代码:
public class Main {
static class A{
int a = 0;
boolean f = false;
}
public static void main(String[] args) throws InterruptedException {
Thread.sleep(5000);//这里为什么要延迟5s,上文解释了。
A a = new A();
System.out.println(ClassLayout.parseInstance(a).toPrintable(a));
Thread t1 = new Thread(() -> {
synchronized (a) {
System.out.println(Thread.currentThread().getName() + " 进入偏向锁状态");
System.out.println(ClassLayout.parseInstance(a).toPrintable(a));
}
});
t1.start();
t1.join();//保证t1线程执行结束后执行main线程,模拟交替运行,此时会发生锁升级
Thread t2 = new Thread(() -> {
try {
Thread.sleep(100);//保证main线程先获取a锁,然后竞争a锁
synchronized (a){
System.out.println(Thread.currentThread().getName() + " 进入重量锁状态");
System.out.println(ClassLayout.parseInstance(a).toPrintable(a));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t2.start();
synchronized (a){
System.out.println(Thread.currentThread().getName() + " 进入轻量锁状态");
System.out.println(ClassLayout.parseInstance(a).toPrintable(a));
Thread.sleep(2000);
}
}
}
运行结果如下:
我们观察红色下划线的数字,这3个为Mark Word中关于锁的3个二进制位,第一个为偏向符号,后两位为锁状态。
1.首先代码中我们延迟5s,保证进入可偏向的状态(101),否则刚开始打印出来结果为(001)。
2.后续启动一个线程t1,拿去a锁, 变成有锁已偏向状态(101)。
3.线程t1启动完成后,让主线程拿去a锁,变成轻量锁状态(000).
4.此时主线程在休眠2000ms,锁未释放,线程t2去争夺锁a,发生不同互斥,此时锁升级为重量级锁(010)。
Main$A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int A.a 0
16 1 boolean A.f false
17 7 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes totalThread-0 进入偏向锁状态
Main$A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 28 1c 21 (00000101 00101000 00011100 00100001) (555493381)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int A.a 0
16 1 boolean A.f false
17 7 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes totalmain 进入轻量锁状态
Main$A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 68 f8 74 02 (01101000 11111000 01110100 00000010) (41220200)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int A.a 0
16 1 boolean A.f false
17 7 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes totalThread-1 进入重量锁状态
Main$A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 3a 28 62 1c (00111010 00101000 01100010 00011100) (476194874)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int A.a 0
16 1 boolean A.f false
17 7 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total
Process finished with exit code 0