java - x86 上的 volatile 怎么会丢失更新?

标签 java x86 volatile

我尝试使用“count”作为 volatile 来运行以下代码:

ExecutorService e = Executors.newFixedThreadPool(2);
for (int i=0; i<2; i++)
{
    e.execute(new Runnable(){
        public void run() {
            for (int i=0; i<1000000; i++)
            count++;
            }
        }
    );
}
e.shutdown();
e.awaitTermination(1, TimeUnit.DAYS);
System.out.println(count);

count 通常小于 1,000,000。

我在 x86 处理器上运行 - Intel core 2 duo E8400,热点为 1.6.24。 与 volatile 变量一起使用的++ 运算符的常见丢失更新参数,目的是实现原子更新,如下所示: 线程 1 和 2 都为 v 读取值 0,都将其递增 1 并写入值 1。

在 x86 上使用 volatile 时,这个论点似乎站不住脚,因为:

1) 对 volatile 变量的每次访问都通过 CPU 缓存层次结构。 JVM 可以生成汇编代码来多次访问 volatile,而无需从内存中加载/存储,只有当它可以证明 volatile 变量是由单个线程访问时,但这里不是这种情况。

2) 只有一个 CPU 可以有一个特定的缓存行处于修改状态,所以如果两个 CPU 都尝试增加 v,只有一个会成功地使包含 v 的缓存行进入修改状态。另一个将使其缓存行无效,稍后才会进入修改状态,其缓存包含正确的值 1,并将变量更新为 2。

我在这里错过了什么?

最佳答案

您忽略了++ 不是原子操作这一事实。

如果您将代码重写为:

for (int i=0; i<1000000; i++)
    int tmp = count;
    tmp = tmp + 1;
    count = tmp;
}

这样是不是更清楚了?这里不需要内存模型或高速缓存行的详细信息 - 我们只需要两个线程都读取相同的值,都进行独立的工作,然后都再次存储它们的计算值。

关于java - x86 上的 volatile 怎么会丢失更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8333043/

相关文章:

java - Wildfly 8.1.0 进行 EJB 远程调用时出错

c - 解释一下这个程序中的esp-ebp

c - 使用 `setp` 而不是 `setb` 有优势吗?

java - volatile 对象工作

java - 使用构建器/工厂模式确保内存可见性

java - 我应该在 `Thread.currentThread().interrupt()` 之前做 `throw new InterruptedIOException()` 吗?

java - 为什么会发生 Full GC Continuous?

x86 - 在 x86 机器码中调用绝对指针

c# - c# 中的 volatile 字段实际上保证了什么?

java - 无法从属性文件返回值 - Java