news 2026/2/25 9:51:47

Synchronized 详解及 JDK 版本优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Synchronized 详解及 JDK 版本优化

1. Synchronized 基础

1.1 Synchronized 的使用方式

1.1.1 修饰实例方法
public class SynchronizedMethod { // 修饰实例方法,锁是当前实例对象(this) public synchronized void instanceMethod() { // 临界区代码 System.out.println("实例方法锁"); } }
1.1.2 修饰静态方法
public class SynchronizedStatic { // 修饰静态方法,锁是当前类的Class对象 public static synchronized void staticMethod() { // 临界区代码 System.out.println("静态方法锁"); } }
1.1.3 修饰代码块
public class SynchronizedBlock { private final Object lock = new Object(); public void method() { // 对象锁 synchronized (this) { // 临界区代码 } // 类锁 synchronized (SynchronizedBlock.class) { // 临界区代码 } // 自定义对象锁 synchronized (lock) { // 临界区代码 } } }

2. Synchronized 实现原理

2.1 对象头结构

在 JVM 中,对象在内存中的布局分为三部分:

  1. 对象头(Header)

  2. 实例数据(Instance Data)

  3. 对齐填充(Padding)

对象头包含以下信息:

32位 JVM 对象头布局
|--------------------------------------------------------------| | Mark Word (32 bits) | |--------------------------------------------------------------| | HashCode (25 bits) | Age (4 bits) | Biased (1 bit) | Normal (01) | |--------------------------------------------------------------| | ThreadID (23 bits) | Epoch (2 bits) | Age (4 bits) | Biased (1 bit) | Biased (01) | |--------------------------------------------------------------| | Lock Pointer (30 bits) | Heavyweight (00) | |--------------------------------------------------------------| | Unused (30 bits) | GC (11) | |--------------------------------------------------------------|
64位 JVM 对象头布局
|------------------------------------------------------------------------------| | Mark Word (64 bits) | |------------------------------------------------------------------------------| | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 | Normal |------------------------------------------------------------------------------| | thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | Biased |------------------------------------------------------------------------------| | ptr_to_lock_record:62 | lock:2 | Lightweight Locked |------------------------------------------------------------------------------| | ptr_to_heavyweight_monitor:62 | lock:2 | Heavyweight Locked |------------------------------------------------------------------------------| | | lock:2 | GC |------------------------------------------------------------------------------|

2.2 Monitor 机制

public class MonitorMechanism { /** * 每个Java对象都与一个Monitor关联 * Monitor包含以下组件: * 1. Owner:当前持有锁的线程 * 2. EntryList:等待获取锁的线程队列 * 3. WaitSet:调用了wait()的线程队列 */ public synchronized void method() { // 编译后的字节码包含以下指令: // monitorenter - 尝试获取Monitor所有权 // 临界区代码 // monitorexit - 释放Monitor } }

2.3 字节码分析

public class BytecodeAnalysis { public synchronized void syncMethod() { System.out.println("synchronized method"); } public void syncBlock() { synchronized (this) { System.out.println("synchronized block"); } } } // 编译后的字节码示例: /* syncMethod: // 方法级的同步在常量池中标记 flags: ACC_PUBLIC, ACC_SYNCHRONIZED syncBlock: monitorenter // 获取锁 ... // 代码块内容 monitorexit // 释放锁 */

3. JDK 版本优化历程

3.1 JDK 1.0 - 1.5:重量级锁时代

public class HeavyweightLockJDK15 { /** * JDK 1.5及之前:Synchronized是完全的重量级锁 * 缺点: * 1. 性能差:需要操作系统内核态切换 * 2. 无法中断:线程一直阻塞 * 3. 非公平锁 * * 工作流程: * 1. 线程请求锁 * 2. 系统调用,从用户态切换到内核态 * 3. 操作系统管理线程阻塞和唤醒 * 4. 再次切换回用户态 */ public static void demonstrate() { Object lock = new Object(); Thread t1 = new Thread(() -> { synchronized (lock) { System.out.println("线程1获取锁"); try { Thread.sleep(3000); // 模拟长时间持有 } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(() -> { synchronized (lock) { System.out.println("线程2获取锁"); } }); t1.start(); t2.start(); // t2会一直阻塞,无法中断 } }

3.2 JDK 1.6:锁优化革命

3.2.1 偏向锁(Biased Locking)
public class BiasedLockingJDK16 { /** * 偏向锁优化: * 1. 针对只有一个线程访问同步块的场景 * 2. 消除无竞争情况下的同步开销 * 3. 延迟到有竞争时才升级 * * 启用参数:-XX:+UseBiasedLocking(JDK 15之前默认开启) */ public static void testBiasedLock() { Object obj = new Object(); // 第一次获取锁,JVM会启用偏向锁 synchronized (obj) { System.out.println("第一次加锁,启用偏向锁"); } // 同一线程再次获取锁,直接进入(无需CAS) synchronized (obj) { System.out.println("同一线程再次加锁,直接进入"); } // 另一个线程尝试获取锁,触发偏向锁撤销 new Thread(() -> { synchronized (obj) { System.out.println("其他线程加锁,撤销偏向锁升级为轻量级锁"); } }).start(); } /** * 偏向锁工作流程: * 1. 检查对象头中的偏向锁标志和锁标志位 * 2. 如果可偏向(01),检查线程ID是否指向当前线程 * 3. 是:直接进入同步块 * 4. 否:尝试CAS替换线程ID * 5. 成功:偏向当前线程 * 6. 失败:存在竞争,升级为轻量级锁 */ }
3.2.2 轻量级锁(Lightweight Locking)
public class LightweightLockingJDK16 { /** * 轻量级锁优化: * 1. 针对线程交替执行同步块的场景 * 2. 使用CAS操作避免操作系统阻塞 * 3. 在用户态完成锁获取 * * 核心机制:对象头Mark Word复制到线程栈帧的锁记录中 */ public static void testLightweightLock() { Object lock = new Object(); // 线程交替执行,使用轻量级锁 for (int i = 0; i < 2; i++) { new Thread(() -> { for (int j = 0; j < 3; j++) { synchronized (lock) { System.out.println(Thread.currentThread().getName() + " 第" + j + "次获取锁"); try { Thread.sleep(100); // 短时间持有 } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } /** * 轻量级锁加锁流程: * 1. 在栈帧中创建锁记录(Lock Record) * 2. 将对象头Mark Word复制到锁记录(Displaced Mark Word) * 3. CAS尝试将对象头指向锁记录 * 4. 成功:获取轻量级锁 * 5. 失败:检查是否重入,否则升级为重量级锁 * * 解锁流程: * 1. CAS将Displaced Mark Word写回对象头 * 2. 成功:解锁完成 * 3. 失败:说明已升级为重量级锁,需要唤醒等待线程 */ } }
3.2.3 自旋锁与适应性自旋
public class SpinLockJDK16 { /** * 自旋锁优化: * 1. 避免线程在用户态和内核态之间切换 * 2. 线程在等待时执行忙循环(自旋) * * 适应性自旋(Adaptive Spinning): * 1. 根据前一次自旋等待时间动态调整 * 2. 如果自旋很少成功,则减少自旋次数 * 3. 如果自旋经常成功,则增加自旋次数 * * 参数: * -XX:+UseSpinning // 启用自旋(JDK 1.6默认) * -XX:PreBlockSpin=10 // 默认自旋次数 */ public static void demonstrateAdaptiveSpin() { Object lock = new Object(); // 线程1长时间持有锁 new Thread(() -> { synchronized (lock) { try { System.out.println("线程1长时间持有锁"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); // 给线程1启动时间 try { Thread.sleep(100); } catch (InterruptedException e) {} // 线程2尝试获取锁 new Thread(() -> { long start = System.currentTimeMillis(); synchronized (lock) { long end = System.currentTimeMillis(); System.out.println("线程2等待时间:" + (end - start) + "ms"); // 第一次自旋可能失败,JVM会记录并减少后续自旋时间 } }).start(); } }
3.2.4 锁消除(Lock Elimination)
public class LockEliminationJDK16 { /** * 锁消除优化: * 1. JIT编译器进行的优化 * 2. 基于逃逸分析(Escape Analysis) * 3. 消除不可能存在共享资源竞争的锁 * * 启用参数: * -XX:+DoEscapeAnalysis // 逃逸分析(默认开启) * -XX:+EliminateLocks // 锁消除(默认开启) */ // 示例1:StringBuffer局部变量 public String concatStrings(String s1, String s2, String s3) { // StringBuffer是线程安全的,但这里的sb不会逃逸出方法 StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); sb.append(s3); return sb.toString(); // JIT会消除synchronized锁 } // 示例2:无竞争的同步块 public void noContention() { Object lock = new Object(); // 局部对象,不会逃逸 synchronized (lock) { // 只有一个线程能访问此方法,锁会被消除 System.out.println("无竞争的锁会被消除"); } } // 对比:锁不会被消除的情况 private final Object sharedLock = new Object(); // 共享对象 public void withContention() { synchronized (sharedLock) { // 多个线程可能竞争,锁不会消除 System.out.println("共享锁不会被消除"); } } }
3.2.5 锁粗化(Lock Coarsening)
public class LockCoarseningJDK16 { /** * 锁粗化优化: * 1. 将连续的多个锁操作合并为一个 * 2. 减少锁的获取和释放次数 * 3. 提升性能(尤其在循环中) */ // 优化前:多次锁获取/释放 public void beforeCoarsening() { Object lock = new Object(); // 连续的同步块 synchronized (lock) { System.out.println("操作1"); } synchronized (lock) { System.out.println("操作2"); } synchronized (lock) { System.out.println("操作3"); } // JIT可能会优化为: // synchronized (lock) { // System.out.println("操作1"); // System.out.println("操作2"); // System.out.println("操作3"); // } } // 循环中的锁粗化 public void loopCoarsening() { Object lock = new Object(); // 在循环内加锁(可能被粗化到循环外) for (int i = 0; i < 1000; i++) { synchronized (lock) { // 简单操作 System.out.println(i); } } // JIT可能优化为: // synchronized (lock) { // for (int i = 0; i < 1000; i++) { // System.out.println(i); // } // } } // 注意:锁粗化的边界条件 public void coarseningBoundary() { Object lock = new Object(); // 中间有耗时操作,可能不会被粗化 synchronized (lock) { System.out.println("操作1"); } expensiveOperation(); // 耗时操作 synchronized (lock) { System.out.println("操作2"); } // 这种情况JIT通常不会粗化 } private void expensiveOperation() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }

3.3 JDK 8:进一步优化

public class SynchronizedJDK8 { /** * JDK 8 优化: * 1. 改进的锁消除和锁粗化 * 2. 更好的逃逸分析 * 3. 默认开启所有优化 * * 重要参数变化: * -XX:+DoEscapeAnalysis // 默认开启 * -XX:+EliminateLocks // 默认开启 * -XX:+EliminateAllocations // 标量替换,默认开启 */ // 标量替换(Scalar Replacement)示例 public void scalarReplacement() { Point point = new Point(10, 20); int x = point.x; // 直接使用基本类型,对象被消除 int y = point.y; System.out.println(x + y); } static class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } } }

3.4 JDK 15:偏向锁被废弃

public class BiasedLockDeprecationJDK15 { /** * JDK 15 重要变化: * 1. 默认禁用偏向锁:-XX:-UseBiasedLocking * 2. 废弃偏向锁相关代码 * 3. 未来版本可能移除 * * 原因: * 1. 现代应用竞争激烈,偏向锁很少受益 * 2. 偏向锁撤销开销大 * 3. 保持偏向锁状态需要额外的维护 * 4. 与新的ZGC不兼容 */ public static void showCurrentDefaults() { // JDK 15+ 默认配置: // -XX:+UseBiasedLocking false // -XX:+UseHeavyMonitors false(使用轻量级锁) System.out.println("JDK 15+ 偏向锁默认禁用"); System.out.println("锁升级路径:无锁 → 轻量级锁 → 重量级锁"); } }

4. 现代 JVM 锁状态转换

4.1 完整的锁升级流程(JDK 15+)

public class ModernLockUpgrade { /** * 现代JVM锁状态转换(JDK 15+): * * 1. 无锁状态 (01) * ↓ 线程请求锁 * 2. 轻量级锁 (00) ←→ 无锁 * ↓ 竞争加剧 * 3. 重量级锁 (10) * * 注意:偏向锁已被废弃 */ // 演示锁升级过程 public static void demonstrateLockUpgrade() { Object lock = new Object(); // 阶段1:无竞争,使用轻量级锁 new Thread(() -> { synchronized (lock) { System.out.println("线程1:轻量级锁"); } }).start(); try { Thread.sleep(100); } catch (InterruptedException e) {} // 阶段2:轻微竞争,可能保持轻量级锁 for (int i = 0; i < 3; i++) { new Thread(() -> { synchronized (lock) { System.out.println("线程" + Thread.currentThread().getId() + ":可能轻量级锁"); } }).start(); try { Thread.sleep(50); } catch (InterruptedException e) {} } // 阶段3:激烈竞争,升级为重量级锁 try { Thread.sleep(1000); } catch (InterruptedException e) {} for (int i = 0; i < 10; i++) { new Thread(() -> { synchronized (lock) { System.out.println("线程" + Thread.currentThread().getId() + ":可能重量级锁"); try { Thread.sleep(100); // 模拟工作 } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } }

5. 性能对比与最佳实践

5.1 性能对比测试

public class SynchronizedPerformance { private static final int ITERATIONS = 10_000_000; private int counter = 0; private final Object lock = new Object(); // 测试1:无同步(基准) public void testNoSync() { for (int i = 0; i < ITERATIONS; i++) { counter++; } } // 测试2:方法级同步 public synchronized void testMethodSync() { for (int i = 0; i < ITERATIONS; i++) { counter++; } } // 测试3:代码块同步 public void testBlockSync() { for (int i = 0; i < ITERATIONS; i++) { synchronized (this) { counter++; } } } // 测试4:粗粒度同步 public void testCoarseSync() { synchronized (this) { for (int i = 0; i < ITERATIONS; i++) { counter++; } } } public static void main(String[] args) { SynchronizedPerformance test = new SynchronizedPerformance(); // 测试不同场景下的性能 long start, end; // 单线程测试 start = System.currentTimeMillis(); test.testNoSync(); end = System.currentTimeMillis(); System.out.println("无同步: " + (end - start) + "ms"); // 重置 test.counter = 0; start = System.currentTimeMillis(); test.testMethodSync(); end = System.currentTimeMillis(); System.out.println("方法同步: " + (end - start) + "ms"); // 更多测试... } }

5.2 最佳实践

public class SynchronizedBestPractices { // 1. 尽量减小同步范围 public void minimizeScope() { // 不好:同步整个方法 // public synchronized void process() { /* 大量代码 */ } // 好:只同步必要部分 public void process() { // 非同步代码 // ... synchronized (this) { // 只同步共享资源访问 } // 更多非同步代码 } } // 2. 使用不同的锁对象 public class SeparateLocks { private final Object readLock = new Object(); private final Object writeLock = new Object(); private int readCount = 0; private int writeCount = 0; public void incrementRead() { synchronized (readLock) { readCount++; } } public void incrementWrite() { synchronized (writeLock) { writeCount++; } } } // 3. 避免在循环内同步 public void avoidSyncInLoop() { Object lock = new Object(); List<String> data = new ArrayList<>(); // 不好:每次迭代都获取/释放锁 // for (String item : data) { // synchronized (lock) { // process(item); // } // } // 好:批量处理 synchronized (lock) { for (String item : data) { process(item); } } } // 4. 使用private final锁对象 public class PrivateFinalLock { private final Object lock = new Object(); // 私有final,防止外部修改 public void safeMethod() { synchronized (lock) { // 线程安全 } } // 避免使用String/Integer等作为锁 // private final String badLock = "LOCK"; // 可能导致意外同步 } // 5. 考虑使用java.util.concurrent public void considerConcurrentPackage() { // 高竞争场景:使用ReentrantLock // ReentrantLock lock = new ReentrantLock(); // lock.lock(); // try { // // 临界区 // } finally { // lock.unlock(); // } // 读写分离:使用ReadWriteLock // ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); } private void process(String item) { // 处理逻辑 } }

6. 监控与调试

6.1 查看锁状态

public class LockStateInspection { // 使用jstack查看线程状态 public static void inspectLockState() { Object lock = new Object(); Thread t1 = new Thread(() -> { synchronized (lock) { try { System.out.println("线程1持有锁"); Thread.sleep(5000); // 长时间持有 } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(() -> { synchronized (lock) { System.out.println("线程2获取到锁"); } }); t1.start(); try { Thread.sleep(100); } catch (InterruptedException e) {} t2.start(); // 此时运行:jstack <pid> 可以看到: // "Thread-0" 状态:TIMED_WAITING (sleeping),持有锁 // "Thread-1" 状态:BLOCKED (on object monitor),等待锁 } // 使用ManagementFactory监控 public static void monitorWithJMX() throws Exception { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); // 查找死锁 long[] deadlockedThreads = threadMXBean.findDeadlockedThreads(); if (deadlockedThreads != null) { ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads); for (ThreadInfo info : threadInfos) { System.out.println("死锁线程: " + info.getThreadName()); System.out.println("锁信息: " + info.getLockInfo()); System.out.println("锁拥有者: " + info.getLockOwnerName()); } } } }

6.2 诊断工具

# 1. jstack - 查看线程栈和锁信息 jstack <pid> # 2. jcmd - 多功能诊断 jcmd <pid> Thread.print # 等同于jstack jcmd <pid> VM.flags # 查看VM参数 # 3. JConsole - 图形化监控 jconsole # 4. VisualVM - 高级分析 jvisualvm # 5. arthas - 阿里开源的Java诊断工具 # 在线诊断,无需重启应用

7. 总结

7.1 优化历程总结

JDK版本主要优化影响
1.0-1.5重量级锁性能差,需要内核切换
1.6偏向锁、轻量级锁、自旋锁、锁消除、锁粗化性能大幅提升
8改进逃逸分析,默认开启所有优化进一步优化
15默认禁用偏向锁简化锁实现,更好适应现代应用

7.2 当前最佳配置(JDK 17+)

# 现代JVM推荐配置 -XX:+UseCompressedOops # 压缩指针(默认开启) -XX:+DoEscapeAnalysis # 逃逸分析(默认开启) -XX:+EliminateLocks # 锁消除(默认开启) -XX:+EliminateAllocations # 标量替换(默认开启) -XX:-UseBiasedLocking # 禁用偏向锁(JDK 15+默认)

7.3 性能建议

  1. 优先使用轻量级同步:volatile、原子类

  2. 合理使用synchronized:减小同步范围

  3. 高竞争场景:考虑ReentrantLock

  4. 读写分离:使用ReadWriteLock

  5. 监控和测试:定期检查锁竞争情况

Synchronized经过多年发展,已经从一个性能较差的重量级锁,优化为智能高效的同步机制。理解其工作原理和优化历程,有助于编写高性能的并发程序。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/22 12:14:12

Java计算机毕设之基于SpringBoot社区医疗预约挂号平台的设计与实现基于springboot的医院挂号就诊系统设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/2/24 20:28:43

如何跟上当今AI高速发展的步伐

如何跟上当今AI高速发展的步伐&#xff0c;这正是我们时代最关键的问题之一。跟上AI时代的步伐&#xff0c;并非要每个人都成为技术专家&#xff0c;而是要建立一种“AI优先”的思维模式和行动策略。以下是一套从思想到行动的系统性建议&#xff0c;希望能为你提供清晰的路径&a…

作者头像 李华
网站建设 2026/2/19 21:27:44

Android 命令行打包 APK 完全指南|极速构建不求人

告别 Android Studio 漫长等待&#xff0c;一行命令 30 秒完成 APK 打包&#xff01;本文详解 Gradle 命令行构建的所有技巧。 前言 每次用 Android Studio 打包 APK&#xff0c;你是不是都要经历&#xff1a; 点击 Build → Generate Signed Bundle / APK选择 APK&#xff0…

作者头像 李华
网站建设 2026/2/24 2:41:44

[STM32C0] 【STM32C092RC 测评】ADC

了解一下ADC先对ADC进行一定的认识分辨率&#xff0c;读出的数据的长度&#xff0c;如8位就是最大值为255的意思&#xff0c;即范围[0,255],12位就是最大值为4096&#xff0c;即范围[0,4096] 通道&#xff0c;ADC输入引脚&#xff0c;通常一个ADC控制器控制多个通道&#xff0…

作者头像 李华
网站建设 2026/2/23 2:17:00

实验四 ysy

/* project1_add.增加数据 */ #include <stdio.h> #include <stdlib.h>typedef struct {int id; // 产地IDchar name[50]; // 产地名称int yield; // 产量&#xff08;吨&#xff09; } OrangeFarm;int main() {OrangeFarm new_farm; // 本次只需定义一个结…

作者头像 李华
网站建设 2026/2/19 4:15:34

拥抱大数据领域数据服务,迎接数字时代挑战

拥抱大数据领域数据服务&#xff0c;迎接数字时代挑战 引言&#xff1a;数字时代的“数据焦虑” 凌晨两点&#xff0c;某零售企业的IT主管王磊盯着电脑屏幕上的报警信息发愁——营销部门刚发来紧急需求&#xff1a;“明天要推出‘618预热活动’&#xff0c;需要近30天所有门店的…

作者头像 李华