Java 并发编程:synchronized 优化原理深度解析
深入解析 Java synchronized 的优化原理。介绍了 synchronized 的互斥性、可见性和有序性作用,以及基于 Monitor 机制的底层实现。重点阐述了偏向锁、轻量级锁、重量级锁的三级锁优化机制及状态转换流程。此外,还讲解了锁消除与锁粗化等 JVM 层优化技术。最后提供了实践建议,包括避免临界区耗时操作、利用锁优化特性及合理控制锁粒度,以提升并发性能。

深入解析 Java synchronized 的优化原理。介绍了 synchronized 的互斥性、可见性和有序性作用,以及基于 Monitor 机制的底层实现。重点阐述了偏向锁、轻量级锁、重量级锁的三级锁优化机制及状态转换流程。此外,还讲解了锁消除与锁粗化等 JVM 层优化技术。最后提供了实践建议,包括避免临界区耗时操作、利用锁优化特性及合理控制锁粒度,以提升并发性能。

synchronized 是 Java 并发编程中最基础且核心的同步机制,用于保证临界区代码的原子性、可见性和有序性。早期 synchronized 因性能开销较大被称为'重量级锁',但 JVM 通过偏向锁、轻量级锁、重量级锁的三级锁机制进行了深度优化,在不同并发场景下自动切换锁状态,平衡了线程安全与执行效率。本文基于 Java 并发编程核心知识,结合具体代码示例与 JVM 底层实现,详细拆解 synchronized 的优化原理与实践逻辑。
synchronized 的同步语义依赖Java 对象头和Monitor(管程) 实现:
synchronized 可修饰实例方法、静态方法和代码块,以下是三种使用方式的核心示例:
public class SynchronizedBasicDemo {
// 1. 修饰实例方法(锁对象:当前实例 this)
public synchronized void instanceMethod() {
System.out.println("实例方法同步:" + Thread.currentThread().getName());
try {
Thread.sleep(100); // 模拟业务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 2. 修饰静态方法(锁对象:SynchronizedBasicDemo.class)
public static synchronized void staticMethod() {
System.out.println("静态方法同步:" + Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 3. 修饰代码块(灵活指定锁对象)
private final Object lock = new Object(); // 自定义锁对象
public void codeBlockMethod() {
synchronized (lock) { // 锁自定义对象
System.out.println("代码块同步(自定义锁):" + Thread.currentThread().getName());
}
synchronized (this) { // 锁当前实例
System.out.println("代码块同步(this 锁):" + Thread.currentThread().getName());
}
synchronized (SynchronizedBasicDemo.class) { // 锁类对象
System.out.println("代码块同步(类锁):" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
SynchronizedBasicDemo demo = new SynchronizedBasicDemo();
// 测试实例方法(同一实例互斥,不同实例不互斥)
new Thread(demo::instanceMethod, "实例线程 1").start();
new Thread(demo::instanceMethod, "实例线程 2").start();
new Thread(new SynchronizedBasicDemo()::instanceMethod, "实例线程 3").start();
// 测试静态方法(全局互斥)
new Thread(SynchronizedBasicDemo::staticMethod, "静态线程 1").start();
new Thread(SynchronizedBasicDemo::staticMethod, "静态线程 2").start();
// 测试代码块
new Thread(demo::codeBlockMethod, "代码块线程").start();
}
}
关键说明:
JVM 根据并发竞争强度,自动切换 synchronized 的锁状态,从低开销到高开销依次为:偏向锁 → 轻量级锁 → 重量级锁,以下结合代码示例解析各阶段特性。
大多数场景下,锁由同一线程多次获取,无并发竞争。偏向锁通过'标记线程 ID'的方式,避免每次获取锁都进行 CAS 操作,降低开销。
import org.openjdk.jol.info.ClassLayout;
public class BiasLockDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
// JVM 默认延迟 4 秒启用偏向锁,此处设置延迟为 0(需添加 JVM 参数:-XX:BiasedLockingStartupDelay=0)
System.out.println("初始状态(无锁):");
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
// 单线程多次获取锁,触发偏向锁
synchronized (lock) {
System.out.println("\n第一次加锁(偏向锁):");
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
}
// 同一线程再次获取锁,偏向锁直接生效
synchronized (lock) {
System.out.println("\n第二次加锁(偏向锁复用):");
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
}
}
}
运行结果关键信息:
当其他线程尝试竞争偏向锁时,会触发撤销流程,代码示例如下:
public class BiasLockRevokeDemo {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
// 线程 1 先获取锁,触发偏向锁
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println("线程 1 持有偏向锁");
try {
Thread.sleep(1000); // 保持锁持有状态
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "Thread-1");
t1.start();
t1.join(); // 等待线程 1 释放锁
// 线程 2 尝试获取锁,触发偏向锁撤销
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("线程 2 竞争锁,偏向锁撤销");
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
}
}, "Thread-2");
t2.start();
}
}
关键说明:
多个线程交替获取锁,无长时间持有锁的情况,避免重量级锁的内核态切换开销。
public class LightweightLockDemo {
private static final Object lock = new Object();
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
// 两个线程交替获取锁,触发轻量级锁
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
synchronized (lock) {
count++; // 临界区代码(执行时间短)
}
}
}, "Thread-1");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
synchronized (lock) {
count++;
}
}
}, "Thread-2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最终计数:" + count);
System.out.println("锁状态(轻量级锁):");
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
}
}
关键说明:
轻量级锁竞争时,线程会通过自旋尝试获取锁,代码示例如下(模拟自旋场景):
public class SpinLockDemo {
private static final Object lock = new Object();
private static int count = 0;
public static void main(String[] args) {
// 3 个线程交替竞争锁,自旋优化生效
for (int i = 0; i < 3; i++) {
new Thread(() -> {
for (int j = 0; j < 500; j++) {
synchronized (lock) {
count++; // 临界区执行时间短,自旋可成功获取锁
}
}
System.out.println(Thread.currentThread().getName() + "执行完成");
}, "Thread-" + i).start();
}
}
}
关键说明:
多个线程同时竞争锁,且锁持有时间较长,自旋优化无法提升效率。
public class HeavyweightLockDemo {
private static final Object lock = new Object();
private static int count = 0;
public static void main(String[] args) {
// 10 个线程同时竞争锁,且临界区执行时间长,触发重量级锁
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
synchronized (lock) {
count++;
try {
Thread.sleep(50); // 模拟长时间持有锁
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
System.out.println(Thread.currentThread().getName() + "执行完成");
}, "Thread-" + i).start();
}
}
}
运行结果关键信息:
| 锁状态转换 | 触发场景 |
|---|---|
| 无锁 → 偏向锁 | 线程首次获取锁,无竞争 |
| 偏向锁 → 轻量级锁 | 其他线程尝试竞争偏向锁 |
| 偏向锁 → 无锁 | 持有偏向锁的线程执行完毕,无其他线程竞争 |
| 轻量级锁 → 重量级锁 | 自旋失败,或多个线程同时竞争轻量级锁 |
| 轻量级锁 → 无锁 | 线程释放锁,无其他线程竞争 |
| 重量级锁 → 无锁 | 线程释放锁,无其他线程竞争 |
当轻量级锁竞争超过自旋阈值后,会触发锁膨胀,具体流程如下:
BLOCKED。Owner 设置为 null,同时唤醒 EntryList 中处于 BLOCKED 状态的线程(如 Thread-1)参与锁竞争。
JVM 通过逃逸分析,识别出不会被多线程共享的局部对象,自动消除其 synchronized 锁。
public class LockEliminationDemo {
// 局部对象仅当前线程使用,JVM 会消除 synchronized 锁
public String processString(String str) {
// new Object() 是局部对象,无逃逸,锁被消除
synchronized (new Object()) {
return str.toUpperCase() + "-" + System.currentTimeMillis();
}
}
public static void main(String[] args) {
LockEliminationDemo demo = new LockEliminationDemo();
// 单线程执行,锁消除优化生效
for (int i = 0; i < 10000; i++) {
demo.processString("test" + i);
}
System.out.println("执行完成(锁消除已生效)");
}
}
关键说明:
-XX:+EliminateLocks 开启锁消除(JDK8 默认开启),-XX:-EliminateLocks 禁用。new Object() 创建的对象仅在方法内使用,无线程共享,锁操作冗余,JVM 会自动消除。将多个连续的细粒度锁合并为一个粗粒度锁,减少锁获取和释放的次数。
public class LockCoarseningDemo {
private final Object lock = new Object();
private StringBuilder sb = new StringBuilder();
// 循环内多次获取锁,JVM 会将锁粗化到循环外部
public void appendStrings(String... strs) {
for (String str : strs) {
synchronized (lock) { // 细粒度锁
sb.append(str);
}
}
}
public static void main(String[] args) {
LockCoarseningDemo demo = new LockCoarseningDemo();
demo.appendStrings("a", "b", "c", "d", "e");
System.out.println(demo.sb.toString());
}
}
关键说明:
| 锁状态 | 适用场景 | 获取锁开销 | 释放锁开销 | 并发性能 | 代码特征 |
|---|---|---|---|---|---|
| 偏向锁 | 单线程重复获取 | 极低(仅检查线程 ID) | 无(不主动释放) | 最高 | 单线程执行同步块,无竞争 |
| 轻量级锁 | 低并发交替获取 | 低(CAS 操作) | 低(CAS 操作) | 中 | 多线程交替执行短临界区 |
| 重量级锁 | 高并发竞争 | 高(内核态阻塞) | 高(内核态唤醒) | 低 | 多线程同时执行长临界区 |
-XX:-UseBiasedLocking 禁用偏向锁,避免偏向锁撤销的开销。减少锁粒度:将大临界区拆分为多个小临界区,使用不同的锁对象,提升并发度(如 ConcurrentHashMap 的分段锁思想)。
// 优化前:一个锁控制多个逻辑
synchronized (lock) {
createOrder();
updateStock();
}
// 优化后:细粒度锁,逻辑并行执行
synchronized (orderLock) {
createOrder();
}
synchronized (stockLock) {
updateStock();
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online