volatile适用于单变量一写多读无依赖状态标志,synchronized用于需原子性的复合操作;二者在DCL单例中分工协作,而非叠加优化,错误结合如volatile修饰被synchronized保护的变量纯属冗余。

Java 中 synchronized 和 volatile 不是“搭配使用”来提升并发效果的工具,而是各司其职、边界清晰的两种机制。强行混用不仅不会带来性能或安全增益,反而容易掩盖设计问题、降低可读性。所谓“最佳并发效果”,关键在于**按场景选对机制,不滥用、不叠加、不误解**。
什么时候该用 volatile
volatile 适合单变量、一写多读、无依赖关系的状态标志:
- 控制线程启停:如 private volatile boolean running = true;,配合 while(!running) 循环退出
- 双重检查锁(DCL)中的实例引用:private static volatile Singleton instance;,防止 new Singleton() 过程被重排序,让其他线程不会看到半初始化对象
- 发布不可变配置或开关:如 private volatile String configVersion;,只允许主线程更新,多个工作线程只读
什么时候该用 synchronized
synchronized 用于需要原子性保障的复合操作,它天然带可见性和有序性:
- 计数器自增:count++ 是读-改-写三步,必须用 synchronized 或 AtomicInteger
- 多变量协同更新:如账户转账,需同时扣 A 账户、加 B 账户,两个动作必须一起成功或失败
- 集合操作:向 ArrayList 添加元素并更新 size 字段,这类非原子组合必须加锁
唯一被广泛认可的“协同”场景:DCL 单例
这是 volatile + synchronized 分工协作的经典范例,不是“优化叠加”,而是逻辑分层:
立即学习“Java免费学习笔记(深入)”;
- 外层 if (instance == null) 利用 volatile 的可见性快速判断,避免每次调用都进锁——减少竞争
- 内层 synchronized 块确保初始化过程严格串行执行一次——保证原子性
- volatile 修饰 instance 字段,阻止构造函数指令重排(如先赋值引用、后执行构造体),避免其他线程拿到未初始化完成的对象
哪些“结合”是错误且多余的
以下写法常见但毫无意义,应避免:
- private volatile int counter; + synchronized(this) { counter++; }:synchronized 已保证可见性,volatile 完全冗余
- 用 volatile 标记一个被 synchronized 保护的 flag 变量:flag 的读写都在临界区内,volatile 不起作用,还误导读者以为“这里要轻量同步”
- 在 synchronized 方法里再给参数或局部变量加 volatile:volatile 不能修饰局部变量,编译直接报错


















