如何在x86上使用volatile发生丢失更新?

Ami*_*adi 3 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);
Run Code Online (Sandbox Code Playgroud)

计数通常最终不到1,000,000.

我正在使用x86处理器 - 英特尔酷睿2双核E8400,热点1.6.24.为了实现原子更新,与volatile运算符一起使用的++运算符通常会丢失更新参数,如下所示:线程1和2都为v读取值0,两者都将其递增1并写入值1.

在x86上使用volatile时,这个论点似乎分崩离析,因为:

1)每次访问volatile变量都会通过CPU缓存层次结构.只有当JVM能够证明单个线程访问volatile变量时,JVM才能生成汇编代码以多次访问volatile,而不会从内存中加载/存储,这不是这里的情况.

2)只有一个CPU可以在修改状态特定的缓存行,所以如果两个CPU试图增加V,只有一个会在获得含钒进入修改状态的高速缓存行成功.另一个将使其高速缓存行无效,并且稍后将进入修改状态,其高速缓存包含正确的值1并将该变量更新为2.

我在这里错过了什么?

Jon*_*eet 5

你错过了++不是原子操作的事实.

如果您将代码重写为:

for (int i=0; i<1000000; i++)
    int tmp = count;
    tmp = tmp + 1;
    count = tmp;
}
Run Code Online (Sandbox Code Playgroud)

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