Java 并发大坑volatile、synchronized、Lock 三者如何选择很多 Java 程序员对volatile、synchronized和Lock的理解停留在“背八股”层面volatile 保证可见性synchronized 重量级Lock 更灵活。但真正写并发代码时选错一个线上就是事故。本文从原理 → 适用场景 → 典型坑点 → 选择决策表一次性讲清楚三者的正确打开方式。一、先给结论速查版场景推荐单纯状态标志如停止线程✅volatile复合操作i、check-then-act❌volatile/ ✅synchronized/ ✅Lock单线程写、多线程读✅volatile临界区保护、简单互斥✅synchronized首选需要公平锁、可中断、超时、多条件队列✅LockReentrantLock高并发 低竞争✅synchronizedJVM 优化好高并发 高竞争 复杂控制✅Lock一句话原则能用volatile就别用锁能用synchronized就别用Lock。二、volatile最容易被误用的关键字1️⃣ volatile 到底解决了什么volatile 只解决两个问题✅可见性✅禁止指令重排序❌不保证原子性// 错误示例 private volatile int count 0; public void increment() { count; // 非原子操作 }count实际是三步读取 countcount 1写回 count多线程下必然出问题。2️⃣ volatile 的正确使用姿势✅ 场景一状态标志最常见 最正确class Worker implements Runnable { private volatile boolean running true; public void stop() { running false; } Override public void run() { while (running) { // do work } } }✔ 一个线程写多个线程读✔ 无复合操作✔ 完美匹配 volatile✅ 场景二双重检查锁定DCLclass Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }⚠️没有 volatile 可能返回半初始化对象3️⃣ volatile 的经典误区❌ 误区 1volatile可以替代锁❌ 误区 2volatile i是线程安全的❌ 误区 3volatile一定比锁快在竞争激烈时未必三、synchronized被低估的王者1️⃣ synchronized 做了什么synchronized保证✅ 原子性✅ 可见性✅ 有序性临界区内本质互斥 内存屏障2️⃣ JVM 对 synchronized 的优化非常重要很多人还停留在“synchronized 是重量级锁”的旧认知里。现代 JVMJDK 8已经实现了锁升级机制无锁 → 偏向锁 → 轻量级锁 → 重量级锁低竞争几乎无开销高竞争自动升级性能可控在大多数业务系统中synchronized 性能优于 Lock3️⃣ synchronized 的最佳实践public class Counter { private int count 0; public synchronized void increment() { count; } public synchronized int getCount() { return count; } }✔ 简单✔ 安全✔ 不易出错4️⃣ synchronized 的局限性❌ 无法响应中断❌ 无法尝试获取锁tryLock❌ 无法实现公平锁❌ 条件队列只有一个wait/notify当你需要这些能力时才考虑Lock。四、Lock功能最强但最危险1️⃣ Lock 的核心优势ReentrantLock lock new ReentrantLock(); lock.lock(); try { // 临界区 } finally { lock.unlock(); }✅ 可中断lockInterruptibly✅ 超时获取锁tryLock(timeout)✅ 公平锁✅ 多条件变量Condition2️⃣ Lock 的典型使用场景✅ 场景一可中断锁lock.lockInterruptibly(); try { // 处理任务 } finally { lock.unlock(); }用于防止死锁、响应线程中断✅ 场景二多条件队列Condition notFull lock.newCondition(); Condition notEmpty lock.newCondition();这是synchronized wait/notify做不到的。3️⃣ Lock 的大坑90% 的人踩过❌忘记 unlocklock.lock(); doSomething(); // 抛异常 → 锁永远不释放✅ 必须放在finally❌重复加锁导致死锁lock.lock(); lock.lock(); // 忘了解锁一次❌性能反而更差在低竞争场景下Lock的性能通常不如synchronized。五、三者对比总结面试 实战必看特性volatilesynchronizedLock原子性❌✅✅可见性✅✅✅有序性部分✅✅可重入❌✅✅可中断❌❌✅公平锁❌❌✅多条件队列❌❌✅复杂度⭐⭐⭐⭐⭐⭐⭐推荐程度慎用⭐⭐⭐⭐⭐⭐⭐⭐六、选择决策流程图建议收藏是否只是状态标志 ├─ 是 → volatile └─ 否 是否需要复杂锁控制中断/超时/公平/多条件 ├─ 是 → Lock └─ 否 → synchronized七、真实线上事故案例警示 案例volatile i 导致库存超卖private volatile int stock 100; public void reduceStock() { stock--; }结果✈️ 库存扣成负数 资损事故原因volatile 不保证原子性✅ 正确做法synchronized (this) { stock--; }或AtomicInteger stock new AtomicInteger(100); stock.decrementAndGet();八、终极建议并发编程的第一原则是不要自己发明并发控制。能用不可变对象就不用 volatile能用synchronized就不用Lock能用并发容器就不用手写锁能用AtomicXXX就不用synchronized九、一句话总结volatile 管“看见”synchronized 管“互斥”Lock 管“精细控制”。能不用锁就不用锁能用简单锁就不用复杂锁。
Java 并发大坑:volatile、synchronized、Lock 三者如何选择?
Java 并发大坑volatile、synchronized、Lock 三者如何选择很多 Java 程序员对volatile、synchronized和Lock的理解停留在“背八股”层面volatile 保证可见性synchronized 重量级Lock 更灵活。但真正写并发代码时选错一个线上就是事故。本文从原理 → 适用场景 → 典型坑点 → 选择决策表一次性讲清楚三者的正确打开方式。一、先给结论速查版场景推荐单纯状态标志如停止线程✅volatile复合操作i、check-then-act❌volatile/ ✅synchronized/ ✅Lock单线程写、多线程读✅volatile临界区保护、简单互斥✅synchronized首选需要公平锁、可中断、超时、多条件队列✅LockReentrantLock高并发 低竞争✅synchronizedJVM 优化好高并发 高竞争 复杂控制✅Lock一句话原则能用volatile就别用锁能用synchronized就别用Lock。二、volatile最容易被误用的关键字1️⃣ volatile 到底解决了什么volatile 只解决两个问题✅可见性✅禁止指令重排序❌不保证原子性// 错误示例 private volatile int count 0; public void increment() { count; // 非原子操作 }count实际是三步读取 countcount 1写回 count多线程下必然出问题。2️⃣ volatile 的正确使用姿势✅ 场景一状态标志最常见 最正确class Worker implements Runnable { private volatile boolean running true; public void stop() { running false; } Override public void run() { while (running) { // do work } } }✔ 一个线程写多个线程读✔ 无复合操作✔ 完美匹配 volatile✅ 场景二双重检查锁定DCLclass Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }⚠️没有 volatile 可能返回半初始化对象3️⃣ volatile 的经典误区❌ 误区 1volatile可以替代锁❌ 误区 2volatile i是线程安全的❌ 误区 3volatile一定比锁快在竞争激烈时未必三、synchronized被低估的王者1️⃣ synchronized 做了什么synchronized保证✅ 原子性✅ 可见性✅ 有序性临界区内本质互斥 内存屏障2️⃣ JVM 对 synchronized 的优化非常重要很多人还停留在“synchronized 是重量级锁”的旧认知里。现代 JVMJDK 8已经实现了锁升级机制无锁 → 偏向锁 → 轻量级锁 → 重量级锁低竞争几乎无开销高竞争自动升级性能可控在大多数业务系统中synchronized 性能优于 Lock3️⃣ synchronized 的最佳实践public class Counter { private int count 0; public synchronized void increment() { count; } public synchronized int getCount() { return count; } }✔ 简单✔ 安全✔ 不易出错4️⃣ synchronized 的局限性❌ 无法响应中断❌ 无法尝试获取锁tryLock❌ 无法实现公平锁❌ 条件队列只有一个wait/notify当你需要这些能力时才考虑Lock。四、Lock功能最强但最危险1️⃣ Lock 的核心优势ReentrantLock lock new ReentrantLock(); lock.lock(); try { // 临界区 } finally { lock.unlock(); }✅ 可中断lockInterruptibly✅ 超时获取锁tryLock(timeout)✅ 公平锁✅ 多条件变量Condition2️⃣ Lock 的典型使用场景✅ 场景一可中断锁lock.lockInterruptibly(); try { // 处理任务 } finally { lock.unlock(); }用于防止死锁、响应线程中断✅ 场景二多条件队列Condition notFull lock.newCondition(); Condition notEmpty lock.newCondition();这是synchronized wait/notify做不到的。3️⃣ Lock 的大坑90% 的人踩过❌忘记 unlocklock.lock(); doSomething(); // 抛异常 → 锁永远不释放✅ 必须放在finally❌重复加锁导致死锁lock.lock(); lock.lock(); // 忘了解锁一次❌性能反而更差在低竞争场景下Lock的性能通常不如synchronized。五、三者对比总结面试 实战必看特性volatilesynchronizedLock原子性❌✅✅可见性✅✅✅有序性部分✅✅可重入❌✅✅可中断❌❌✅公平锁❌❌✅多条件队列❌❌✅复杂度⭐⭐⭐⭐⭐⭐⭐推荐程度慎用⭐⭐⭐⭐⭐⭐⭐⭐六、选择决策流程图建议收藏是否只是状态标志 ├─ 是 → volatile └─ 否 是否需要复杂锁控制中断/超时/公平/多条件 ├─ 是 → Lock └─ 否 → synchronized七、真实线上事故案例警示 案例volatile i 导致库存超卖private volatile int stock 100; public void reduceStock() { stock--; }结果✈️ 库存扣成负数 资损事故原因volatile 不保证原子性✅ 正确做法synchronized (this) { stock--; }或AtomicInteger stock new AtomicInteger(100); stock.decrementAndGet();八、终极建议并发编程的第一原则是不要自己发明并发控制。能用不可变对象就不用 volatile能用synchronized就不用Lock能用并发容器就不用手写锁能用AtomicXXX就不用synchronized九、一句话总结volatile 管“看见”synchronized 管“互斥”Lock 管“精细控制”。能不用锁就不用锁能用简单锁就不用复杂锁。