Java:关于 Java 关键字的混淆 "volatile" 在 JLS 中解释

Java: confusion about Java keyword "volatile" explained in JLS

JLS中有例子 关于关键字 volatile.

一个线程重复调用方法一,另一个线程调用方法二。

  class Test {
        static volatile int i = 0, j = 0;
        static void one() { i++; j++; }
        static void two() {
            System.out.println("i=" + i + " j=" + j);
        }
    }

This allows method one and method two to be executed concurrently, but guarantees that accesses to the shared values for i and j occur exactly as many times, and in exactly the same order, as they appear to occur during execution of the program text by each thread. Therefore, the shared value for j is never greater than that for i, because each update to i must be reflected in the shared value for i before the update to j occurs. It is possible, however, that any given invocation of method two might observe a value for j that is much greater than the value observed for i, because method one might be executed many times between the moment when method two fetches the value of i and the moment when method two fetches the value of j.

一些问题:

我从其他文章了解到,如果有一个以上的核心或处理器(我认为重点是多个缓存),"unvolatile"值可能会出问题。

有什么问题吗?为什么?

共享值是 static int i,j,因为它们可以在许多不同的线程之间共享(这是静态的定义)。当您有多个线程访问同一个变量时,您应该使用 volatile。关于是否仅当一个线程写入该值而其他线程读取该值时才应使用 volatile 存在同样的争论,因为 i += 5 等操作不是原子的,除非您使用 AtomicInteger(尽管有一个更高的开销)。

当一个线程写入该值而一个(或多个)线程从该值读取时,您应该使用 volatile 的原因是因为如果没有 volatile 关键字,线程将缓存该值并且将使用缓存中的值,该值可能与实际值不一致。 volatile 关键字强制每个线程不缓存该值,而是访问该值的实际内存位置。

此上下文中的共享值是由多个线程操作的变量。

对非易失性变量的访问可以通过实现(编译器或CPU)重新排序,因此结果可能与"naively"预期结果不一致,即顺序执行。使用 volatile,您可以保证对共享变量的访问是串行完成的,与程序顺序执行一致。

在没有锁、信号量或其他互斥原语的保护的情况下,对变量的冲突访问并发进行时,您需要 volatile。如果至少有一个是写入(read/write 或 write/write)并且它们不是按互斥原语或程序顺序排序的,则对同一变量的两个操作是冲突的。