为何子线程无法感知静态变量修改?与JMM或volatile有关吗?
子线程无法感知静态变量修改:JMM与volatile的关系
嘿,这个问题的核心其实是Java内存模型(JMM)的可见性规则,而volatile关键字正是专门用来解决这类可见性问题的。我来一步步给你拆解:
为什么子线程看不到静态变量的修改?
Java内存模型(JMM)为了提升线程执行性能,设计了「工作内存」的概念:
- 所有共享变量(包括静态变量)都存储在主内存中,这是所有线程共享的公共内存空间。
- 每个线程自身拥有一块工作内存,线程读取变量时,会先把主内存里的变量拷贝到自己的工作内存,之后就只操作这个本地副本;修改完成后,再把副本同步回主内存。
问题就出在这个拷贝和同步的环节:当主线程修改了静态变量stop的值后,这个更新不会自动同步到子线程的工作内存。子线程会一直盯着自己工作内存里的旧值(false),完全不知道主内存里的stop已经发生变化,所以while循环就会一直运行下去停不下来。
volatile关键字是怎么解决这个问题的?
volatile给变量加上了两个关键特性,刚好能破解这个可见性难题:
- 强制可见性:只要有线程修改了volatile变量,新值会立刻刷回主内存;其他线程读取这个变量时,必须直接去主内存获取最新值,不能再使用自己工作内存里的旧副本。
- 禁止指令重排序:防止编译器或CPU对涉及volatile的代码进行乱序执行优化,保证代码的执行顺序和你编写的逻辑一致。
结合你的代码来看就非常直观了:
public class Test3{ public static volatile boolean stop = false;// 不加volatile的话,循环大概率停不下来 public static void main(String[] args) throws InterruptedException{ Thread thread = new Thread(){ public void run() { int i=0; while(!stop){ i++; // 加这行偶尔也能让循环停止,但完全不可靠 // System.out.println(i); // 这行也能触发同步,但同样不推荐依赖 // try { // Thread.sleep(1); // } catch (InterruptedException e) { // e.printStackTrace(); // } } } }; thread.start(); Thread.sleep(1000); // 模拟主线程先执行其他逻辑,之后修改stop stop = true; } }
- 没加volatile时,子线程的while循环会持续读取工作内存中的
false副本,主线程修改主存的stop也无法被感知,循环无法停止。 - 加了volatile后,子线程每次判断
!stop都会直接从主内存读取最新值,主线程把stop改成true后,子线程能立刻感知到变化,循环随即退出。
为什么注释里的操作偶尔能让循环停止?
你代码里注释的i++、System.out.println(i)或者Thread.sleep(1),有时候不加volatile也能让循环停下来,这其实是意外的副作用:
System.out.println()内部使用了synchronized锁,进入和退出锁的过程中,线程会强制同步工作内存与主内存的数据,相当于间接刷新了变量值。Thread.sleep()会让线程暂停,部分情况下线程在休眠前后会自动同步工作内存和主内存的内容。- 哪怕是
i++,在某些CPU或编译器的优化逻辑下,也可能偶然触发内存同步,但这完全是随机的,不同环境下结果可能不一致。
这些都是不可靠的“野路子”,生产环境里千万别依赖,老老实实加volatile或者使用其他同步机制(比如synchronized、Lock)才是正确的解决方案。
内容的提问来源于stack exchange,提问作者chenkh
相关产品推荐
相关产品推荐

