1. 理论
锁的状态分为四种:无锁状态、偏向锁、轻量级锁、重量级锁。
锁可以从偏向锁升级为轻量级锁,再升级为重量级锁,但锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级。
锁的状态记录在对象头的Mark Word中,(锁的标志位看后几位bit位)如下:

{tabs-pane 后三位 }
001 代表无锁状态
101 代表偏向锁 {/tabs-pane} {tabs-pane 后两位 } 00 代表轻量级锁
10 代表重量级锁 {/tabs-pane}
101 代表偏向锁 {/tabs-pane} {tabs-pane 后两位 } 00 代表轻量级锁
10 代表重量级锁 {/tabs-pane}
2. 代码验证
引入对象头分析工具
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
打印mark word
// 对象
Object object = new Object();
// 打印对象头mark word
System.out.println(ClassLayout.parseInstance(object).toPrintable());
对象内存区域,红框表示mark word信息

// 打印出来的值(本人用的是windows系统运行,所以这里打印出来的值是小端模式)
00000001 00000000 00000000 00000000
// 转换(大端模式)
00000000 00000000 00000000 00000001
// object对象没有加锁,没有被synchronized修饰,对象头mark word后三位001为无锁状态
无锁 —> 偏向锁
开启偏向锁,当object对象被上锁,并有一个线程执行,object的mark word后三位标志位会升级为偏向锁(101)
代码执行前先配置jvm参数:【开启偏向锁】【关闭延迟开启偏向锁】
-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
Object object = new Object();
System.out.println("============== 加锁前mark word ==============");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
synchronized (object) {
// 业务逻辑...
System.out.println("============== 加锁后mark word ==============");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
}

// ================ 加锁前mark word ================ (转大端模式后)
// 开启偏向锁,默认匿名偏向
00000000 00000000 00000000 00000101
// ================ 加锁后mark word ================ (转大端模式后)
// 偏向某个线程
11110001 10101110 11011000 00000101
偏向锁 —> 轻量级锁
当有两个,及两个以上的线程竞争锁不激烈的情况下(串行执行时),偏向锁会升级为轻量级锁
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
Object object = new Object();
// 没有加锁前
System.out.println("========== 加锁前打印的对象头 ==========");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
// t1线程先进行加锁执行
new Thread(() -> {
synchronized (object) {
System.out.println("========== t1线程打印的对象头==========");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
}).start();
// 等待500毫秒(模拟升级轻量级锁)(模拟竞争不激烈的情况下,t1线程执行完后,t2线程再进行执行)
TimeUnit.MILLISECONDS.sleep(500);
// t2线程再进行加锁执行
new Thread(() -> {
synchronized (object) {
System.out.println("========== t2线程打印的对象头 ==========");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
}).start();
}

// ================ 加锁前打印的mark word ================ (转大端模式后)
00000000 00000000 00000000 00000101 // 匿名偏向
// ================ t1线程打印的mark word ================ (转大端模式后)
10001011 11000011 00000000 00000101 // 偏向锁
// ================ t2线程打印的mark word ================ (转大端模式后)
11100000 01101111 11110101 00010000 // 轻量级锁
重量级锁
当有多个线程竞争锁比较激烈的情况下(都想在同一时刻获取到锁),会直接升级为重量级锁
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
Object object = new Object();
// 没有加锁前
System.out.println("========== 加锁前打印的对象头 ==========");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
// t1线程先进行加锁执行
new Thread(() -> {
synchronized (object) {
System.out.println("========== t1线程打印的对象头 ==========");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
}).start();
// 等待500毫秒(模拟升级轻量级锁)(模拟竞争不激烈的情况下,t1线程执行完后,t2线程再进行执行)
TimeUnit.MILLISECONDS.sleep(500);
// t2线程再进行加锁执行
new Thread(() -> {
synchronized (object) {
System.out.println("========== t2线程打印的对象头 ==========");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
}).start();
// 等待500毫秒,(开始模拟升级重量级锁)开始模拟竞争比较激烈的情况(同一时刻都t3、t4线程都想获取锁)
Thread t3 = new Thread(() -> {
synchronized (object) {
System.out.println("========== t3线程打印的对象头 ==========");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
});
Thread t4 = new Thread(() -> {
synchronized (object) {
System.out.println("========== t4线程打印的对象头 ==========");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
});
t3.start();
t4.start();
}


// ================ 加锁前打印的mark word ================ (转大端模式后)
00000000 00000000 00000000 00000101 // 匿名偏向
// ================ t1线程打印的mark word ================ (转大端模式后)
00111010 00100001 10111000 00000101 // 偏向锁
// ================ t2线程打印的mark word ================ (转大端模式后)
11101001 10011111 11110000 00000000 // 轻量级锁
// ================ t4线程打印的mark word ================ (转大端模式后)
00110110 10100011 11010000 11101010 // 重量级锁
// ================ t3线程打印的mark word ================ (转大端模式后)
00110110 10100011 11010000 11101010 // 重量级锁
评论区