为何MutableInteger非线程安全?stale data问题解析与复现
关于MutableInteger线程不安全与Stale Data的问题解答
嘿,我来帮你把《Java并发编程实战》里这个MutableInteger的问题讲清楚~
一、这个场景下的Stale Data到底指什么?
Stale Data(过期数据)在这里的核心意思是:当一个线程修改了MutableInteger的value值后,其他线程可能无法及时看到这个最新修改,仍然读取到旧的、已经过期的数值。
这背后的原因和Java内存模型有关:每个线程都有自己的工作内存,会把共享变量(比如这里的value)缓存一份到自己的工作内存里。而MutableInteger的value字段既没有用volatile修饰(强制线程读写直接操作主内存),也没有任何同步机制(比如synchronized)来保证内存更新的同步。所以线程A修改value后,这个更新只会留在自己的工作内存,不会同步到主内存;线程B读取value时,还是从自己的工作内存里拿旧的副本,自然就读到了stale data。
二、如何复现Stale Data的场景?
我写一段简单的多线程代码,你运行一下就能直观看到现象:
public class MutableIntegerStaleDemo { private static MutableInteger mutableInt = new MutableInteger(0); public static void main(String[] args) throws InterruptedException { // 线程1:持续读取value,尝试获取最新值 new Thread(() -> { int currentValue = mutableInt.getValue(); while (currentValue < 10) { if (mutableInt.getValue() != currentValue) { currentValue = mutableInt.getValue(); System.out.println("线程1读到最新值:" + currentValue); } } }).start(); // 线程2:每隔1秒更新一次value new Thread(() -> { int currentValue = mutableInt.getValue(); while (currentValue < 10) { mutableInt.setValue(++currentValue); System.out.println("线程2已设置值为:" + currentValue); try { Thread.sleep(1000); // 留时间让线程1读取 } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); Thread.sleep(12000); // 等待线程执行完成 } } // 就是书中的MutableInteger类 class MutableInteger { private int value; public MutableInteger(int value) { this.value = value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
运行后你会发现:线程2每隔1秒就会把value加1,但线程1可能完全读不到这些更新,一直停留在初始的0,直到程序结束——这就是stale data导致的典型现象。
三、多线程使用MutableInteger会引发哪些问题?
除了上面的可见性问题(stale data),还会带来这些隐患:
- 原子性缺失:如果多个线程做复合操作(比如
mutableInt.setValue(mutableInt.getValue() + 1)),会出现竞态条件。比如两个线程同时读到value=5,都加1后设置为6,而不是预期的7——因为getValue()和setValue()是两个独立的操作,没有原子性保证。 - 程序行为不可预测:因为线程间的数据不同步,程序的执行结果会依赖于线程调度的时机,有时候看似正常,有时候出现错误,排查起来非常困难。
- 数据一致性破坏:如果依赖MutableInteger的数值做业务逻辑判断,比如判断value是否达到阈值来执行某个操作,可能因为读到stale data,导致逻辑迟迟不触发,或者触发时机错误。
内容的提问来源于stack exchange,提问作者Coder-Man
相关产品推荐
相关产品推荐

