可见性指一个线程修改共享变量后其他线程能否立即看到,源于工作内存与主内存同步延迟;有序性指代码顺序与实际执行顺序可能因编译器/CPU重排序而不同,volatile通过内存屏障禁止重排序并保证可见性。

多线程环境下的可见性与有序性,本质是线程间“看到什么”和“按什么顺序看到”的问题。不是代码写成什么样就一定执行成什么样,而是受硬件缓存、编译器优化和 JVM 内存模型共同影响的结果。
可见性:一个线程改了,另一个线程能不能立刻“看见”
Java 把内存逻辑上分成主内存(所有线程共享)和每个线程私有的工作内存(类似 CPU 缓存或寄存器)。线程操作变量时,先从主内存把值拷贝到自己的工作内存里,改完再写回去。
问题就出在这里:线程 A 修改了变量并写回主内存,但线程 B 可能一直用自己工作内存里的旧副本,根本不去主内存重新读——它就“看不见”变化。
- 典型表现:一个线程循环等待 flag == true,另一个线程设了 flag = true,前者却卡死不动
- 根本原因:JIT 优化可能把 flag 读取提升为常量,或者 CPU 缓存未及时同步
- 解决方式:用 volatile 修饰 flag,强制每次读都去主内存取,每次写都立刻刷回主内存
有序性:代码写的先后,不等于实际执行的先后
为了性能,编译器和 CPU 可能对指令重排序。只要单线程结果不变,它们就可以调整执行顺序。但在多线程下,这种“合法优化”可能破坏逻辑依赖。
立即学习“Java免费学习笔记(深入)”;
比如创建单例对象的三步:
① 分配内存 → ② 初始化对象 → ③ 把地址赋给静态变量 instance
若重排成 ①→③→②,另一个线程看到 instance 不为空,就直接返回,结果拿到一个还没初始化完成的对象。
- volatile 不仅保证可见性,还插入内存屏障,禁止它前后的指令重排序
- synchronized 块内的操作也遵循 happens-before 规则,天然具备有序性保障
- 单纯靠加法、赋值等普通操作,无法约束跨线程的执行顺序
两者其实是一体两面
可见性和有序性经常交织出现。比如 volatile 写操作不仅让值“可见”,还确保它之前的写操作对后续读该 volatile 变量的线程也可见——这就是 happens-before 关系在起作用。
没有同步机制时,你不能假设: - “我刚改了 x,对方马上能看到”(可见性失效) - “我先设了 flag,再改了 data,对方一定先看到 flag 再看到 data”(有序性失效)
真正可靠的协作,必须依靠 JMM 提供的语义边界,比如 volatile、synchronized、final 字段初始化,或显式使用 Lock 和原子类。


















