在 JDK 17 上完整观察 synchronized 锁升级过在 JDK 17 上完整观察 synchronized 锁升级过
1、锁升级流程图解
2、代码实现
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.concurrent.CountDownLatch;
public class SupermarketLockDemo {
private static final Unsafe UNSAFE = getUnsafe();
// 共享锁对象
static final Object lock = new Object();
static final CountDownLatch latch = new CountDownLatch(10);
private static Unsafe getUnsafe() {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
return (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
throw new RuntimeException("无法获取Unsafe实例", e);
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== JDK 17 锁升级演示(完全可靠版) ===");
System.out.println("JVM详情: " + VM.current().details());
// 1. 初始无锁状态 (001)
System.out.println("\n=== 阶段1: 初始无锁状态 ===");
printLockState(lock);
// 2. 主线程首次获取锁 (偏向锁)
synchronized (lock) {
System.out.println("\n=== 阶段2: 主线程偏向锁 ===");
printLockState(lock);
}
// 3. 轻量级锁演示
Thread lightThread = new Thread(() -> {
synchronized (lock) {
System.out.println("\n=== 阶段3: 线程1轻量级锁 ===");
printLockState(lock);
}
});
lightThread.start();
lightThread.join();
// 4. 重量级锁演示
System.out.println("\n=== 阶段4: 重量级锁触发 ===");
for (int i = 0; i < 10; i++) {
new Thread(() -> {
synchronized (lock) {
// 第一个获得锁的线程打印
if (latch.getCount() == 10) {
System.out.println("\n=== 阶段4: 重量级锁状态 ===");
printLockState(lock);
}
latch.countDown();
try { Thread.sleep(100); } catch (Exception e) {}
}
}).start();
}
latch.await();
System.out.println("\n演示完成");
}
// 直接使用Unsafe获取对象头信息
private static void printLockState(Object obj) {
// 打印JOL布局
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
try {
// 获取对象地址
long address = VM.current().addressOf(obj);
// 读取对象头(Mark Word)
long markWord = UNSAFE.getLong(obj, 0L);
// 提取最后3位锁状态
int lockBits = (int)(markWord & 0b111);
System.out.println("对象内存地址: 0x" + Long.toHexString(address));
System.out.println("Mark Word值: 0x" + Long.toHexString(markWord));
System.out.println("锁状态位: " + padBinary(lockBits, 3));
System.out.println("状态说明: " + getLockStateName(lockBits));
} catch (Exception e) {
System.err.println("解析错误: " + e.getMessage());
// 尝试从JOL输出中提取锁状态
String layout = ClassLayout.parseInstance(obj).toPrintable();
parseLockStateFromString(layout);
}
}
// 从字符串解析锁状态作为后备方案
private static void parseLockStateFromString(String layout) {
try {
// 查找包含"(object header: mark)"的行
for (String line : layout.split("\n")) {
if (line.contains("(object header: mark)")) {
// 提取十六进制部分
String hexPart = line.split("0x")[1].split("\\)")[0].trim();
// 解析十六进制字符串
long markWord = Long.parseLong(hexPart, 16);
// 提取最后3位锁状态
int lockBits = (int)(markWord & 0b111);
System.out.println("从字符串解析的锁状态: " + padBinary(lockBits, 3));
System.out.println("状态说明: " + getLockStateName(lockBits));
return;
}
}
System.out.println("⚠️ 警告: 未能解析锁状态");
} catch (Exception e) {
System.err.println("字符串解析失败: " + e.getMessage());
}
}
// 补全二进制位数
private static String padBinary(int value, int bits) {
return String.format("%" + bits + "s", Integer.toBinaryString(value))
.replace(' ', '0');
}
private static String getLockStateName(int lockBits) {
switch (lockBits) {
case 0b001: return "无锁 (001)";
case 0b101: return "偏向锁 (101)";
case 0b000: return "轻量级锁 (000)";
case 0b010: return "重量级锁 (010)";
case 0b011: return "GC标记 (011)";
default: return "未知状态 (" + padBinary(lockBits, 3) + ")";
}
}
}
D:\application\java17\bin\java.exe -Dcool.request.port=49369 "-javaagent:D:\application\idea\IntelliJ IDEA 2022.3.3\lib\idea_rt.jar=50258:D:\application\idea\IntelliJ IDEA 2022.3.3\bin" -Dfile.encoding=UTF-8 -classpath D:\workplace\gitcode\java-basic\target\classes;C:\Users\浩宸\.m2\repository\org\openjdk\jol\jol-core\0.17\jol-core-0.17.jar;C:\Users\浩宸\.config\.cool-request\request\lib\spring-invoke-starter.jar SupermarketLockDemo -XX:+UnlockDiagnosticVMOptions -XX:+PrintBiasedLockingStatistics -XX:BiasedLockingStartupDelay=0 -XX:+UseBiasedLocking --add-opens java.base/jdk.internal.vm=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.misc=ALL-UNNAMED
=== JDK 17 锁升级演示(完全可靠版) ===
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
JVM详情: # VM mode: 64 bits
# Compressed references (oops): 3-bit shift
# Compressed class pointers: 0-bit shift and 0x800000000 base
# Object alignment: 8 bytes
# ref, bool, byte, char, shrt, int, flt, lng, dbl
# Field sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array base offsets: 16, 16, 16, 16, 16, 16, 16, 16, 16
=== 阶段1: 初始无锁状态 ===
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x00000d58
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
对象内存地址: 0x7194ff310
Mark Word值: 0x1
锁状态位: 001
状态说明: 无锁 (001)
=== 阶段2: 主线程偏向锁 ===
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000009c4a9ff448 (thin lock: 0x0000009c4a9ff448)
8 4 (object header: class) 0x00000d58
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
对象内存地址: 0x7194ff310
Mark Word值: 0x9c4a9ff448
锁状态位: 000
状态说明: 轻量级锁 (000)
=== 阶段3: 线程1轻量级锁 ===
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000009c4bbff5d0 (thin lock: 0x0000009c4bbff5d0)
8 4 (object header: class) 0x00000d58
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
对象内存地址: 0x7194ff310
Mark Word值: 0x9c4bbff5d0
锁状态位: 000
状态说明: 轻量级锁 (000)
=== 阶段4: 重量级锁触发 ===
=== 阶段4: 重量级锁状态 ===
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x000001ea48a48cb2 (fat lock: 0x000001ea48a48cb2)
8 4 (object header: class) 0x00000d58
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
对象内存地址: 0x7194ff310
Mark Word值: 0x1ea48a48cb2
锁状态位: 010
状态说明: 重量级锁 (010)
演示完成
Process finished with exit code 0
3、各阶段解析说明
- 无锁状态 (001)
对象头初始值: 0x0000000000000001
最后三位: 001 (二进制)
特点: 对象刚创建,未被任何线程使用
- 偏向锁状态 (101)
对象头值: 0x0000000000000005
最后三位: 101 (二进制)
特点: 存储了主线程ID,后续该线程可直接使用
- 轻量级锁状态 (000)
对象头值: 0x0000006a930ff610
最后三位: 000 (二进制)
JOL标识为 (thin lock),表示轻量级锁
特点: 指向线程栈中的锁记录
- 重量级锁状态 (010)
对象头值: 0x0000028e9d00b20a
最后三位: 010 (二进制)
JOL标识为 (fat lock),表示重量级锁
特点: 指向操作系统层面的Monitor对象