volatile轻量但仅保证可见性与有序性,不保证原子性;synchronized重量但全面保障原子性、可见性、有序性,适用于多步依赖或多变量协同场景。

volatile 和 synchronized 都是 Java 内存模型(JMM)中保障多线程安全的关键机制,但它们在性能开销和安全能力上存在明确的取舍:volatile 轻量高效,但能力有限;synchronized 重而全面,但代价更高。
volatile 的性能优势与安全短板
volatile 变量读操作几乎无额外开销,写操作因插入内存屏障(如 StoreStore、StoreLoad)略慢,但仍远低于锁机制。它不涉及线程阻塞、上下文切换或 Monitor 竞争,适合高频读、低频写的场景。
- 只保证可见性和禁止指令重排序,不提供原子性——i++ 这类复合操作仍会出错
- 无法防止竞态条件:多个线程同时读-改-写 volatile 变量时,结果不可预测
- 不能用于保护临界区代码块,仅适用于单个变量的状态同步(如 flag、status)
synchronized 的安全全面性与运行成本
synchronized 通过 Monitor 锁实现互斥,进入时强制刷新工作内存、退出时强制写回主内存,天然覆盖原子性、可见性、有序性三大需求。但它会引发线程排队、挂起/唤醒、锁膨胀等开销,尤其在高竞争场景下延迟明显。
- 支持任意复杂逻辑的原子执行,包括多变量协同更新、条件判断+修改等组合操作
- 锁释放时同步所有共享变量到主内存,确保后续获取该锁的线程看到完整一致状态
- JDK 1.6 后虽有偏向锁、轻量级锁等优化,但本质仍是重量级同步原语,不适合细粒度、高频调用
典型权衡场景举例
比如控制线程启停的开关变量:用 volatile boolean running = true 就足够——只需通知其他线程“状态变了”,无需原子性;但若要实现“检查是否运行中 + 执行一次任务 + 更新计数器”,就必须用 synchronized 或 ReentrantLock 包裹整个逻辑块,否则计数器可能丢失更新。
立即学习“Java免费学习笔记(深入)”;
- 单变量状态标志(如 shutdown、inited)、双重检查锁中的 instance 引用——volatile 合适
- 账户余额增减、缓存更新+版本号递增、生产者消费者信号协调——必须用 synchronized 或更高级同步工具
- 追求极致吞吐且逻辑简单时,可搭配 AtomicInteger 等 CAS 类型替代部分 synchronized 使用
不复杂但容易忽略
选 volatile 还是 synchronized,本质是在“是否需要互斥执行”上做判断。只要代码块内存在多个步骤依赖同一状态,或涉及多个变量协同变更,volatile 就不再安全——这时加锁不是过度设计,而是必要防线。



















