java 使用 volatile long 有什么意义吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 
原文地址: http://stackoverflow.com/questions/3038203/
Warning: these are provided under cc-by-sa 4.0 license.  You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there any point in using a volatile long?
提问by Adamski
I occasionally use a volatileinstance variable in cases where I have two threads reading from / writing to it and don't want the overhead (or potential deadlock risk) of taking out a lock; for example a timer thread periodically updating an int ID that is exposed as a getter on some class:
我偶尔会使用volatile实例变量,以防我有两个线程从中读取/写入它,并且不想要取出锁的开销(或潜在的死锁风险);例如,一个计时器线程会定期更新一个 int ID,该 ID 在某个类上作为 getter 公开:
public class MyClass {
  private volatile int id;
  public MyClass() {
    ScheduledExecutorService execService = Executors.newScheduledThreadPool(1);
    execService.scheduleAtFixedRate(new Runnable() {
      public void run() {
        ++id;
      }
    }, 0L, 30L, TimeUnit.SECONDS);
  }
  public int getId() {
    return id;
  }
}
My question: Given that the JLS only guarantees that 32-bit reads will be atomic is there any point in everusing a volatile long? (i.e. 64-bit).
我的问题:鉴于JLS只能保证32位读取将是原子有任何一点曾经使用挥发性长时间?(即 64 位)。
Caveat: Please do not reply saying that using volatileover synchronizedis a case of pre-optimisation; I am well aware of how / when to use synchronizedbut there are cases where volatileis preferable.  For example, when defining a Spring bean for use in a single-threaded application I tend to favour volatileinstance variables, as there is no guarantee that the Spring context will initialise each bean's properties in the main thread.
警告:请不要回复说使用volatileoversynchronized是预优化的情况;我很清楚如何/何时使用,synchronized但在某些情况下volatile更可取。例如,在定义用于单线程应用程序的 Spring bean 时,我倾向于使用volatile实例变量,因为不能保证 Spring 上下文会在主线程中初始化每个 bean 的属性。
回答by aioobe
Not sure if I understand your question correctly, but the JLS 8.3.1.4. volatile Fieldsstates:
不确定我是否正确理解您的问题,但是JLS 8.3.1.4。volatile 字段指出:
A field may be declared volatile, in which case the Java memory model ensures that all threads see a consistent value for the variable (§17.4).
一个字段可能被声明为 volatile,在这种情况下,Java 内存模型确保所有线程看到变量的一致值(第17.4 节)。
and, perhaps more importantly, JLS 17.7 Non-atomic Treatment of double and long:
也许更重要的是,JLS 17.7 非原子处理 double 和 long:
17.7 Non-atomic Treatment of double and long
[...]
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64 bit value from one write, and the second 32 bits from another write. Writes and reads of volatile long and double values are always atomic.Writes to and reads of references are always atomic, regardless of whether they are implemented as 32 or 64 bit values.
17.7 double 和 long 的非原子处理
[...]
就 Java 编程语言内存模型而言,对非易失性 long 或 double 值的单次写入被视为两次单独的写入:每个 32 位一半。这可能导致线程从一次写入中看到 64 位值的前 32 位,而从另一次写入中看到后 32 位的情况。对 volatile long 和 double 值的写入和读取始终是原子的。对引用的写入和读取始终是原子的,无论它们是作为 32 位值还是 64 位值实现的。
That is, the "entire" variable is protected by the volatile modifier, not just the two parts. This tempts me to claim that it's even more importantto use volatile for longs than it is for ints since not even a readis atomic for non-volatile longs/doubles.
也就是说,“整个”变量受 volatile 修饰符保护,而不仅仅是两部分。这诱使我声称对s使用 volatile比对s更重要,因为对于非易失性 longs/doubles 来说,即使读取也不是原子的。longint
回答by Adam
This can be demonstrated by example
这可以通过例子来证明
- constantly toggle two fields, one marked volatile and one not between all bits set and all bits clear
 - read the field values on another thread
 - see that the foo field (not protected with volatile) can be read in an inconsistent state, this never happens to the bar field protected with volatile
 
- 不断切换两个字段,一个标记为易失性,一个不在所有位设置和所有位清除之间
 - 在另一个线程上读取字段值
 - 看到 foo 字段(不受 volatile 保护)可以在不一致的状态下读取,这不会发生在受 volatile 保护的 bar 字段中
 
Code
代码
public class VolatileTest {
    private long foo;
    private volatile long bar;
    private static final long A = 0xffffffffffffffffl;
    private static final long B = 0;
    private int clock;
    public VolatileTest() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    foo = clock % 2 == 0 ? A : B;
                    bar = clock % 2 == 0 ? A : B;
                    clock++;
                }
            }
        }).start();
        while (true) {
            long fooRead = foo;
            if (fooRead != A && fooRead != B) {
                System.err.println("foo incomplete write " + Long.toHexString(fooRead));
            }
            long barRead = bar;
            if (barRead != A && barRead != B) {
                System.err.println("bar incomplete write " + Long.toHexString(barRead));
            }
        }
    }
    public static void main(String[] args) {
        new VolatileTest();
    }
}
Output
输出
foo incomplete write ffffffff00000000
foo incomplete write ffffffff00000000
foo incomplete write ffffffff
foo incomplete write ffffffff00000000
Note this only happens for me when running on a 32 bit VM, on 64 bit VM I couldn't get a single error in several minutes.
请注意,这仅在我在 32 位 VM 上运行时发生,在 64 位 VM 上,我在几分钟内无法收到一个错误。
回答by Adam
"volatile" serves multiple purposes:
“volatile”有多种用途:
- guarantees atomic writes to double/long
 - guarantees that when a thread A sees change in volatile variable made by thread B, thread A can also see all other changes made by thread B before the change to volatile variable (think setting the number of used cells in array after setting the cells themselves).
 - prevents compiler optimization based on assumption that only one thread can change the variable (think tight loop 
while (l != 0) {}. 
- 保证原子写入 double/long
 - 保证当线程 A 看到线程 B 对 volatile 变量的更改时,线程 A 也可以看到线程 B 在更改 volatile 变量之前所做的所有其他更改(考虑在设置单元格本身之后设置数组中使用的单元格的数量) .
 - 基于只有一个线程可以更改变量的假设来阻止编译器优化(想想紧密循环
while (l != 0) {}. 
Is there more?
还有更多吗?

