JMM 与 volatile

笔记/Java/Java基础/Java并发/JMM 与 volatile

上一篇:生命周期与通信 · 下一篇:synchronized 与 Lock

三大特性#

特性含义典型手段
原子性操作不可被中途打断synchronized、Lock、Atomic、CAS
可见性一个线程修改对其他线程可见volatile、synchronized、Lock
有序性按代码顺序的感知happens-before、volatile 禁重排

i++ 不是原子操作(读-改-写三步)。

面试常问:synchronized 和 volatile 各解决什么问题?#

解决什么不解决什么
synchronized原子性 + 可见性 + 互斥(同一锁下代码块同时只有一个线程)性能开销、可能死锁
volatile可见性 + 禁止部分重排序i++ 等复合操作的原子性

记忆:volatile 管「看得见」;synchronized 管「抢得到锁、改得对」
→ 锁详解 synchronized 与 Lock

Java 内存模型(JMM)#

每个线程有 工作内存,共享变量在主内存。线程读写先拷贝到工作内存,写回主内存才对其他线程可见——中间窗口导致 可见性问题

线程共享 vs 私有#

共享线程私有
堆、方法区(元空间)虚拟机栈、本地方法栈、程序计数器

每个线程有自己的栈;堆上对象可被多线程共享,需同步保护。

volatile 两大作用#

  1. 可见性:写立即刷主内存,读从主内存取最新值
  2. 禁止指令重排序(内存屏障语义),保证部分 happens-before

不能保证复合操作原子性(如 volatile int++ 仍不安全)。

volatile vs synchronized#

volatilesynchronized
原子性仅单次读/写保证临界区
阻塞不阻塞可能阻塞
适用状态标志、DCL 单例的实例引用复合操作、互斥

happens-before 规则(节选)#

  • 程序顺序:同线程前序 happens-before 后序
  • 监视器锁:unlock happens-before 后续 lock
  • volatile:写 happens-before 后续读
  • 线程 start:start happens-before 线程内动作
  • 线程 join:线程内动作 happens-before join 返回

指令重排#

编译器/CPU 为优化可能重排指令。单线程 as-if-serial 结果不变;多线程下可能暴露 bug。volatile / synchronized 限制重排范围。

乐观锁 vs 悲观锁#

  • 悲观锁:认为冲突常发生,先加锁再操作(synchronized、Lock)
  • 乐观锁:认为冲突少,CAS 比较-交换,失败重试

不可变对象#

final 字段正确发布后不可变,读无需额外同步,利于并发安全(如 String、不可变 DTO)。

双重检查锁单例(DCL)#

public class Singleton {
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // 需 volatile 防重排
}
}
}
return instance;
}
}

volatile 时,其他线程可能看到未完全初始化的对象。

一句话总结#

并发 bug 多来自可见性、有序性、原子性三者之一;volatile 管可见性和部分有序性,复合操作要用锁或 Atomic。

文章目录

文章目录