multiThread修改了一个volatile变量,但是这个变量几乎不能接近10000
我几乎整个下午都在思考这个问题。为什么这个程序的 volatile 变量几乎不能接近 10000。代码如下:
public class TestModifyVolatile {
volatile int count = 0;
void m(){
count++;
}
public static void main(String[] args) {
List<Thread> threads = new ArrayList<>();
TestModifyVolatile t = new TestModifyVolatile();
for (int i=0;i<10000;i++){
threads.add(new Thread(t::m,"t_"+i));
}
threads.parallelStream().forEach(e->e.start());
threads.forEach(o->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count);
}
}
大部分结果是9996 9998... 假设线程1先启动,线程1使变量count变为1。在上面描述的过程中。也许另一个线程与线程 1 同时启动,读取变量计数也是 0 并写回 1?这是我的猜测,但我无法证明
回答
count++即使count字段是可变的,增量操作也不是原子操作。实际上,它执行易失性读取,然后是易失性写入。在读取和后续写入之间,其他一些线程可以读取 的(初始)值count。所以你可以有以下交错:
count is 100
thread A: read count -> 100
thread B: read count -> 100
thread A: write 101 to count -> 101
thread B: write 101 to count -> 101
你失去了一个增量。(
请注意,当前 JLS 中没有任何内容明确说明 volatile 增量是非原子的。但是,它遵循 JLS 17.4 为volatile字段的内存可见性属性指定的内容。
不可能使用单个2 volatile字段并且没有额外的同步来实现可靠的(线程安全的)计数器。使用AtomicInteger来代替。
1 - 可能有两个。我不知道。
THE END
二维码