为什么使用 volatile 使 long 和 double atomic

why using volatile makes long and double atomic

我正在尝试学习 Java 中多线程中使用的术语。因此,如果我在以下文本中使用了错误的定义,请纠正我:

我从不同资源中得到的发现

原子操作: 根据 Java 文档:

In programming, an atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side effects of an atomic action are visible until the action is complete.

这就是为什么读取或写入 long 或 double 变量不是原子的。因为它涉及两个操作,第一个32位和第二个32位read/write到变量。另外,从上面的段落中,我了解到如果我在一个方法上使用 synchronized,它将使该方法成为原子的(理论上来说)。

可变变量: 也来自 Java 文档:

This means that changes to a volatile variable are always visible to other threads. What's more, it also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.

现在,同样根据 Joshua BlochEffective Java 第 2 版,考虑书中关于 volatile 声明的以下几点:

考虑以下几点:

// Broken - requires synchronization!
private static volatile int nextSerialNumber = 0;

public static int generateSerialNumber() {
    return nextSerialNumber++;
}

The method’s state consists of a single atomically accessible field, nextSerialNumber, and all possible values of this field are legal. Therefore, no synchronization is necessary to protect its invariants. Still, the method won’t work properly without synchronization.

这是因为 nextSerialNumber++ 不是原子的,因为它执行读取、递增、写入。

我的总结

所以如果 nextSerialNumber++ 不是原子的,并且需要 synchronize。那为什么下面是原子的,不需要同步?

private static volatile long someNumber = 0;

public static int setNumber(long someNumber) {
    return this.someNumber = someNumber;
}

我不明白的是为什么在 doublelong 上使用 volatile 使其成为原子?

因为 volatile 所做的只是确保线程 B 是否尝试读取线程 A 正在写入的 long 变量,并且只有 32 位由线程写入A,然后当线程 B 访问资源时,它会得到线程 A 写入的 32 位数字。这并没有使它成为 atomic 作为术语 atomic 在 Java Doc.

中有解释

What I don't understand is why using volatile on double or long, makes it atomic?

原因如下。将 volatiledoublelong 一起使用会使它们成为原子 因为 JLS 是这样说的 .

JLS (Section 17.7) 指出:

"Writes and reads of volatile long and double values are always atomic."


注意:JLS 是规范的。就语言语义而言,javadoc 是非规范的。 Bloch 的 "Effective Java" 甚至不是 Oracle 文档 - 它只是一个(未经授权的)评论。

What I don't understand is why using volatile on double or long, makes it atomic?

如果不使用 volatile 关键字,您可能会读取一个线程写入的 doublelong 的前 32 位,以及另一个线程写入的其他 32 位,称为单词撕裂,显然不是原子的。

volatile 关键字确保不会发生这种情况。您读取的 64 位值将是一个线程写入的值,而不是多个线程写入结果的 Franken-value。这就是由于 volatile 关键字,这些类型成为原子的意思。

关键字volatile不能使像x++这样的操作成为原子操作,无论类型如何(64位或32位),因为它是一个复合操作(读取+增量+写入),如反对简单的写。 x++ 中涉及的操作可能会被其他线程的操作交错。 volatile 关键字不能使此类复合操作原子化。

So if nextSerialNumber++ is not atomic, and requires synchronize. Then why the following is atomic and doesn't require synchronize?

private static volatile long someNumber = 0;

public static int setNumber(long someNumber) {
    return this.someNumber = someNumber;
}

nextSerialNumber++ 需要同步,因为它是复合操作,因此不是原子操作。

this.someNumber = someNumber 是原子的,因为 this.someNumbervolatile,赋值操作也是原子的,是一个单一的操作。因此不需要同步。如果没有 volatilethis.someNumber 将无法以原子方式编写,因此需要同步。