synchronized和volatile分工明确、互不替代:synchronized保证原子性、可见性和有序性,适用于复合操作;volatile仅保证可见性和有序性,适用于单变量状态标志或dcl单例中防止指令重排。

volatile 解决的是变量读写的可见性与有序性
它适用于:一个线程写、多个线程读的简单状态标志场景。
- 比如:
private volatile boolean running = true;控制线程生命周期 - 再如:
private volatile Singleton instance;在双重检查锁(DCL)单例中防止指令重排导致对象未完全构造就被引用 - 关键点:volatile 只管“这个变量本身”,不管它参与的运算是否安全
synchronized 解决的是复合操作的原子性 + 可见性 + 有序性
它适用于:需要多步操作不可分割、或涉及多个变量协同更新的临界区。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 比如:
count++(读-改-写三步)、list.add(x); size++;这类非原子操作 - 再如:银行账户转账——需同时扣减A余额、增加B余额,两个动作必须一起成功或一起失败
- 关键点:synchronized 锁定的是代码块或方法,保证进入的线程独占执行权
常见误区:不要用 volatile “辅助” synchronized
例如下面这种写法没有意义,且容易误导:
private volatile int counter;
public void increment() {
synchronized (this) {
counter++;
}
}
- counter 已被 synchronized 保护,volatile 完全多余 —— 锁释放时自动刷回主内存,进入时自动重载最新值
- 加了 volatile 反而让人误以为“这里需要轻量同步”,实则混淆了关注点
- JVM 会优化掉无意义的 volatile 语义,但代码可读性和维护性下降
真正有意义的组合场景:DCL 单例中的 volatile + synchronized
这是唯一被广泛认可、有明确语义分工的经典用法:
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) { // 第一次检查(无锁)
synchronized (Singleton.class) { // 加锁
if (instance == null) { // 第二次检查(锁内)
instance = new Singleton(); // 关键:volatile 防止 new 指令重排
}
}
}
return instance;
}
- synchronized 保证创建动作的原子性与排他性
- volatile 保证 instance 引用的可见性 + 禁止 new Singleton() 的指令重排(避免其他线程看到半初始化对象)
- 二者各司其职,缺一不可;不是“配合”,而是“分层协作”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










