volatile专注可见性与有序性,synchronized保障原子性、可见性与有序性;前者适用于状态标志等简单变量,后者用于复合操作的临界区保护,二者各司其职,可协同但不可替代。

volatile 和 synchronized 不是“搭配使用”的关系,而是各司其职、适用场景分明的两种机制。它们可以出现在同一段代码中,但不是为了“互补功能”而配合,而是因为各自解决不同层面的问题——volatile 专注可见性与有序性,synchronized 保障原子性+可见性+有序性。真正需要“配合”的场景,其实是:用 volatile 做轻量级状态通知,用 synchronized 保护临界区操作。
volatile 负责“让别人立刻看到变化”
当一个共享变量仅用于状态标识(如开关、标志位),且写操作本身是原子的(比如布尔赋值、引用赋值),就适合用 volatile:
- 它确保写线程把新值立即刷回主内存;
- 读线程每次读都强制从主内存加载,不依赖工作内存缓存;
- 同时禁止对该变量相关的指令重排序,避免逻辑错乱。
例如:volatile boolean isRunning = true; —— 多个线程轮询该标志,一旦被设为 false,所有线程都能及时感知并退出循环。
synchronized 负责“让操作一步到位不被打断”
当涉及多个步骤的复合操作(如 i++、先查后改、集合增删等),必须用 synchronized(或 Lock)来保证原子性。此时它附带的内存语义也解决了可见性:
立即学习“Java免费学习笔记(深入)”;
- 进入同步块时,清空工作内存,强制从主内存读最新值;
- 退出同步块时,把修改全部刷新回主内存;
- 其他线程后续获取同一把锁时,自然就能看到这些更新。
例如:synchronized(list) { list.add(item); } —— 不仅防止并发 add 导致数据错乱,也确保 list 结构变更对其他线程可见。
典型配合模式:volatile + synchronized 协同分工
常见于“状态驱动+临界操作”结构,比如一个可停止的任务执行器:
- 用 volatile boolean stopped 控制是否继续运行;
- 任务体内部用 synchronized 保护共享资源访问;
- 停止方法里先设 stopped = true(volatile 写),再调用 synchronized 唤醒等待线程或清理资源。
这里 volatile 不是为了“帮 synchronized 更快”,而是让停止信号本身能被即时感知;synchronized 则确保清理动作不被并发干扰——二者解决的是不同环节的正确性问题。
不能靠 volatile “优化” synchronized
有人误以为加 volatile 能减少 synchronized 的开销,这是误区:
- volatile 无法替代锁的互斥能力,对非原子操作(如 count++)加 volatile 毫无意义;
- synchronized 已经自带强可见性保证,再给其中变量加 volatile 属于冗余,JVM 不会因此跳过锁流程;
- 过度使用 volatile 可能因频繁刷缓存反而降低性能,尤其在高写低读场景。
真正要优化,应考虑锁粒度、无锁算法(如 AtomicInteger)、或 JUC 工具类,而非混用关键字。


















