内存可见性指线程对共享变量的修改能否被其他线程及时看到,因JMM中线程操作变量需经工作内存与主内存间复制,无同步时修改可能不及时刷回主内存;volatile通过强制读写主内存及禁止重排序解决该问题,但不保证原子性;synchronized也保证可见性,但以加锁和更大开销为代价,提供原子性保障。
内存可见性,指的是一个线程对共享变量的修改,能被其他线程“及时看到”。它不是“会不会被看到”,而是“什么时候被看到”——不加同步时,flag = true 执行完,另一个线程仍可能永远读到 false。
Java 内存模型(JMM)把内存分成两层:主内存(所有线程共享)和每个线程私有的工作内存。线程操作变量时,并不直接读写主内存,而是:
问题就出在第三步:没有同步机制时,JIT 编译器或 CPU 缓存可能让线程一直用自己工作内存里的旧值,根本不查主内存。比如下面这段代码会死循环:
public class VisibilityDemo {
static boolean run = true;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (run) { } // 空循环
System.out.println("退出");
});
t.start();
Thread.sleep(100);
run = false; // 主线程改了,但 t 线程看不到
}
}
volatile 不是锁,但它强制两点:
volatile 变量时,必须立刻刷新到主内存volatile 变量时,必须从主内存重新加载,禁止使用工作内存缓存只需把上面的 static boolean run = true 改成 static volatile boolean run = true,问题就消失。但注意:volatile 只保可见性和有序性,不保原子性——count++ 这种复合操作仍需 synchronized 或 AtomicInteger。
是的,synchronized 同样具备可见性语义:

区别在于粒度和开销:
volatile 是轻量级、无锁、只作用于单个变量synchronized 是重量级、有锁、作用于代码块或方法,附带互斥(原子性)保障volatile;如果涉及“读-改-写”逻辑(如计数、状态流转),必须用 synchronized 或原子类最容易被忽略的一点:可见性问题往往在高并发下不暴露,而是在低负载、长时间运行或 JIT 优化后才突然复现——所以不能靠“测试没出错”来判断是否安全。