选volatile还是synchronized取决于操作性质:单次写、多次读的布尔标志等用volatile保证可见性与有序性;涉及读-改-写或多个变量协同修改则必须用synchronized保证原子性、可见性与有序性,二者常协作使用。

选 volatile 还是 synchronized,关键看你要解决的是“可见性+有序性”问题,还是“原子性+互斥”问题。不是性能好坏决定用哪个,而是你要保护的操作性质决定该用哪个。
只改一个布尔开关或状态标志?用 volatile
比如停止信号、初始化完成标记、配置开关等——这类变量通常只有单次写、多次读,且写操作本身不依赖当前值。
- 典型场景:线程启动后轮询
volatile boolean running = true,另一个线程设为false终止它;双重检查锁里的volatile Singleton instance - 优势:无锁、无阻塞、开销极小,JVM 仅插入内存屏障,不涉及线程调度
- 注意:不能用于
count++、list.add()这类读-改-写复合操作,哪怕变量本身是 volatile
要保护一段逻辑或多个变量一起变?用 synchronized
只要操作涉及“先读再算再写”,或者需要确保多个变量的修改对其他线程整体可见,就必须上锁。
- 典型场景:银行账户转账(扣减 A 账户 + 增加 B 账户)、缓存更新(先查缓存 + 加载 DB + 写缓存)、计数器自增
- 优势:天然保证原子性、可见性、有序性三者,语义完整,不易出错
- 注意:锁粒度要合理——避免锁整个方法,优先用同步代码块锁定最小临界区;可考虑
synchronized(this)、synchronized(LOCK_OBJ)或静态锁
它们不是替代关系,而是协作关系
实际工程中常配合使用。volatile 解决“通知”问题,synchronized 解决“执行”问题。
立即学习“Java免费学习笔记(深入)”;
- 例子:一个后台任务用
volatile boolean isReady标记初始化完成;真正加载资源、更新状态时,仍需在synchronized块里做,防止多线程重复初始化 - 再如:用 volatile 发布不可变对象引用(如配置对象),但构建该对象的过程必须在同步块内完成,否则可能看到部分构造的对象
别踩这些常见坑
混淆功能边界是最容易引发并发 bug 的原因。
-
误以为 volatile 能防 i++ 竞争:它只让每次读写都直达主存,但
i++是三条指令,中间仍可能被其他线程打断 - 用 synchronized 保护单个 flag 却不必要:纯状态切换用 volatile 更轻量,锁反而引入上下文切换开销
- 忽略锁对象一致性:不同线程用不同锁对象(比如 new Object() 每次新建),等于没锁
- 忘了 volatile 不禁止非 volatile 变量重排序:它只约束自身读写顺序,其他普通变量仍可能被重排到 volatile 操作前后



















