volatile变量同步后,线程1为何能读取非volatile变量stop的正确值?
private static boolean stop = false; private static volatile boolean stop_volatile = false; public static void main(String[] args) { Thread thread0 = new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } stop = true; stop_volatile = true; }); Thread thread1 = new Thread(() -> { while (true) { if (stop_volatile){ System.out.println("thread1:" + stop); break; } } }); Thread thread2 = new Thread(() -> { while (true) { if (stop){ System.out.println("thread2:" + stop); break; } } }); thread0.start(); thread1.start(); thread2.start(); }
问题背景
现有两个静态字段stop和stop_volatile,其中stop_volatile被标记为volatile。thread0将两个字段的值都改为true,thread1可正常停止,thread2则无限循环。
核心疑问:
- 为何thread1会打印“thread1:true”,它为何总能从非volatile字段
stop获取正确值? - thread0修改
stop_volatile后,会将工作缓存写入内存并通知其他线程该变量缓存失效,需重新从内存加载。但stop是非volatile变量,为何thread1未用缓存值而重新加载?
原因解析
这是volatile变量内存语义带来的连锁效果,和Java内存模型(JMM)的规则直接相关:
thread0的写入顺序保证
thread0中先执行stop = true,再执行stop_volatile = true。由于stop_volatile是volatile变量,JMM会触发store-store屏障:这个屏障会阻止普通变量的写入操作被重排序到volatile变量写入之后,同时要求所有在volatile写入前的操作(包括stop = true)必须先同步到主内存。也就是说,thread0修改stop后,会在修改stop_volatile之前就把stop的新值刷到主内存。thread1的读取触发全缓存同步
thread1的循环一直在读取volatile变量stop_volatile,当它检测到stop_volatile变为true时,volatile读取操作会触发load-load和load-store屏障:这两个屏障会强制当前线程的工作缓存失效,所有后续变量读取都必须从主内存重新加载。此时thread1读取stop时,自然会拿到主内存里已经被thread0更新后的true值。thread2无限循环的本质
thread2只读取普通变量stop,JMM没有强制要求它必须从主内存刷新值。JIT编译器会对这种无同步的循环做优化:把stop的值缓存到线程工作内存的寄存器中,每次循环只读取寄存器里的旧值(false),完全不会去主内存同步。所以即便thread0已经把stop改成true,thread2也感知不到,会一直循环下去。
内容的提问来源于stack exchange,提问作者mini shen

