news 2026/8/1 7:56:20

Java 并发大坑:volatile、synchronized、Lock 三者如何选择?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 并发大坑:volatile、synchronized、Lock 三者如何选择?

Java 并发大坑:volatile、synchronized、Lock 三者如何选择?

很多 Java 程序员对volatilesynchronizedLock的理解停留在“背八股”层面:

volatile 保证可见性,synchronized 重量级,Lock 更灵活。

但真正写并发代码时,选错一个,线上就是事故。本文从原理 → 适用场景 → 典型坑点 → 选择决策表,一次性讲清楚三者的正确打开方式。


一、先给结论(速查版)

场景

推荐

单纯状态标志(如停止线程)

volatile

复合操作(i++、check-then-act)

volatile/ ✅synchronized/ ✅Lock

单线程写、多线程读

volatile

临界区保护、简单互斥

synchronized(首选)

需要公平锁、可中断、超时、多条件队列

Lock(ReentrantLock)

高并发 + 低竞争

synchronized(JVM 优化好)

高并发 + 高竞争 + 复杂控制

Lock

👉一句话原则

能用volatile就别用锁;能用synchronized就别用Lock


二、volatile:最容易被误用的关键字

1️⃣ volatile 到底解决了什么?

volatile 只解决两个问题:

可见性

禁止指令重排序

不保证原子性

// 错误示例 private volatile int count = 0; public void increment() { count++; // 非原子操作! }

count++实际是三步:

  1. 读取 count

  2. count + 1

  3. 写回 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


✅ 场景二:双重检查锁定(DCL)
class 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 的经典误区

❌ 误区 1:volatile可以替代锁

❌ 误区 2:volatile i++是线程安全的

❌ 误区 3:volatile一定比锁快(在竞争激烈时未必)


三、synchronized:被低估的王者

1️⃣ synchronized 做了什么?

synchronized保证:

✅ 原子性

✅ 可见性

✅ 有序性(临界区内)

本质:互斥 + 内存屏障


2️⃣ JVM 对 synchronized 的优化(非常重要)

很多人还停留在“synchronized 是重量级锁”的旧认知里。

现代 JVM(JDK 8+)已经实现了锁升级机制:

无锁 → 偏向锁 → 轻量级锁 → 重量级锁
  • 低竞争:几乎无开销

  • 高竞争:自动升级,性能可控

👉在大多数业务系统中,synchronized 性能优于 Lock


3️⃣ 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)

✅ 公平锁

✅ 多条件变量(Condition


2️⃣ Lock 的典型使用场景

✅ 场景一:可中断锁
lock.lockInterruptibly(); try { // 处理任务 } finally { lock.unlock(); }

用于防止死锁、响应线程中断


✅ 场景二:多条件队列
Condition notFull = lock.newCondition(); Condition notEmpty = lock.newCondition();

这是synchronized + wait/notify做不到的。


3️⃣ Lock 的大坑(90% 的人踩过)

忘记 unlock

lock.lock(); doSomething(); // 抛异常 → 锁永远不释放

✅ 必须放在finally


重复加锁导致死锁

lock.lock(); lock.lock(); // 忘了解锁一次

性能反而更差

在低竞争场景下,Lock的性能通常不如synchronized


五、三者对比总结(面试 & 实战必看)

特性

volatile

synchronized

Lock

原子性

可见性

有序性

部分

可重入

可中断

公平锁

多条件队列

复杂度

⭐⭐

⭐⭐⭐⭐

推荐程度

慎用

⭐⭐⭐⭐⭐

⭐⭐⭐


六、选择决策流程图(建议收藏)

是否只是状态标志? ├─ 是 → 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 管“精细控制”。

能不用锁就不用锁,能用简单锁就不用复杂锁。

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

多团队共用 Claude API,Key 管理该怎么规范

在一家公司里,如果好几个团队都开始接入 Claude API,真正容易出问题的,往往不是“接口怎么调”,而是“Claude API Key 到底该怎么管”。很多团队刚开始为了图方便,会把同一个 Key 发给研发、运营、测试、外包同学&…

作者头像 李华
网站建设 2026/8/1 7:47:11

图解DES、3DES与AES:从原理到实战的对称加密算法指南

1. 项目概述:为什么我们需要图解加密算法? 在数字世界里,数据就像一封封需要邮寄的明信片,谁都能看到上面的内容。而加密算法,就是给这张明信片装上一个只有你和收件人才能打开的密码锁。今天我们不谈枯燥的数学公式&a…

作者头像 李华
网站建设 2026/8/1 7:46:54

VRRP协议深度解析:从原理到实战的高可用网络网关冗余方案

1. 项目概述:为什么我们需要VRRP? 在网络运维的日常里,最怕听到的词可能就是“单点故障”。想象一下,你公司所有员工上网的流量都汇聚到一台核心路由器上,这台设备一旦宕机或者需要维护重启,整个办公网络瞬…

作者头像 李华
网站建设 2026/8/1 7:44:31

docker-image 工具展示更详细镜像层内容

docker-image 工具展示更详细镜像层内容 作为全栈工程师,日常开发中我们频繁使用 Docker 构建、推送和部署镜像。但 docker history 和 docker inspect 这类内置命令在排查镜像体积、层内容、历史变更时,往往信息不够直观:层大小不明确、命令…

作者头像 李华