java Java中的可变变量
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6259745/
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
Volatile variable in Java
提问by denniss
So I am reading this book titled Java Concurrency in Practiceand I am stuck on this one explanation which I cannot seem to comprehend without an example. This is the quote:
所以我正在阅读这本书,标题是Java Concurrency in Practice并且我坚持这个解释,如果没有例子,我似乎无法理解。这是报价:
When thread
A
writes to a volatile variable and subsequently threadB
reads that same variable, the values of all variables that were visible toA
prior to writing to the volatile variable become visible toB
after reading the volatile variable.
当线程
A
写入 volatile 变量并随后线程B
读取该相同变量时,在A
写入 volatile 变量之前可见的所有变量的值B
在读取 volatile 变量后变为可见。
Can someone give me a counterexample of why "the values of ALL variables that were visible to A
prior to writing to the volatile variable become visible to B
AFTER reading the volatile variable"?
有人能给我一个反例,为什么“在A
写入 volatile 变量之前可见的所有变量的值在B
读取 volatile 变量之后变得可见”?
I am confused why all other non-volatile variables do not become visible to B
before reading the volatile variable?
我很困惑为什么所有其他非易失性变量B
在读取易失性变量之前都看不到?
采纳答案by Brett Kail
Thread B may have a CPU-local cache of those variables. A read of a volatile variable ensures that any intermediate cache flush from a previous write to the volatile is observed.
线程 B 可能有这些变量的 CPU 本地缓存。对 volatile 变量的读取可确保观察到从先前写入到 volatile 的任何中间缓存刷新。
For an example, read the following link, which concludes with "Fixing Double-Checked Locking using Volatile":
例如,请阅读以下链接,该链接以“使用 Volatile 修复双重检查锁定”结尾:
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
回答by sgokhales
Declaring a volatile Java variable means:
声明一个 volatile Java 变量意味着:
- The value of this variable will never be cached thread-locally: all reads and writes will go straight to "main memory".
- Access to the variable acts as though it is enclosed in a synchronized block, synchronized on itself.
- 此变量的值永远不会在线程本地缓存:所有读取和写入都将直接进入“主内存”。
- 对变量的访问就像它被包含在一个同步块中一样,它本身是同步的。
Just for your reference, When is volatile needed ?
仅供参考,什么时候需要 volatile ?
When multiple threads using the same variable, each thread will have its own copy of the local cache for that variable. So, when it's updating the value, it is actually updated in the local cache not in the main variable memory. The other thread which is using the same variable doesn't know anything about the values changed by the another thread. To avoid this problem, if you declare a variable as volatile, then it will not be stored in the local cache. Whenever thread are updating the values, it is updated to the main memory. So, other threads can access the updated value.
当多个线程使用同一个变量时,每个线程将拥有该变量的本地缓存副本。因此,当它更新值时,它实际上是在本地缓存中而不是在主变量内存中更新。使用相同变量的另一个线程对另一个线程更改的值一无所知。为了避免这个问题,如果你将一个变量声明为 volatile,那么它就不会被存储在本地缓存中。每当线程更新值时,它都会更新到主内存。因此,其他线程可以访问更新后的值。
From JLS §17.4.7Well-Formed Executions
来自JLS §17.4.7 格式良好的执行
We only consider well-formed executions. An execution E = < P, A, po, so, W, V, sw, hb > is well formed if the following conditions are true:
Each read sees a write to the same variable in the execution. All reads and writes of volatile variables are volatile actions. For all reads r in A, we have W(r) in A and W(r).v = r.v. The variable r.v is volatile if and only if r is a volatile read, and the variable w.v is volatile if and only if w is a volatile write.
Happens-before order is a partial order. Happens-before order is given by the transitive closure of synchronizes-with edges and program order. It must be a valid partial order: reflexive, transitive and antisymmetric.
The execution obeys intra-thread consistency. For each thread t, the actions performed by t in A are the same as would be generated by that thread in program-order in isolation, with each write wwriting the value V(w), given that each read r sees the value V(W(r)). Values seen by each read are determined by the memory model. The program order given must reflect the program order in which the actions would be performed according to the intra-thread semantics of P.
The execution is happens-before consistent (§17.4.6).
The execution obeys synchronization-order consistency. For all volatile reads r in A, it is not the case that either so(r, W(r)) or that there exists a write win A such that w.v = r.v and so(W(r), w) and so(w, r).
我们只考虑格式良好的执行。如果满足以下条件,则执行 E = < P, A, po, so, W, V, sw, hb > 是良构的:
每次读取都会在执行中看到对同一变量的写入。所有对 volatile 变量的读取和写入都是 volatile 操作。对于 A 中的所有读 r,我们在 A 中有 W(r) 和 W(r).v = rv 变量 rv 是易失性的当且仅当 r 是易失性读,而变量 wv 是易失性的当且仅当 w是易失性写入。
Happens-before order 是偏序。Happens-before 顺序是由同步边和程序顺序的传递闭包给出的。它必须是一个有效的偏序:自反、传递和反对称。
执行遵循线程内一致性。对于每个线程 t,t 在 A 中执行的操作与该线程在程序顺序中独立生成的操作相同,每次写入 w 写入值 V(w),假设每次读取 r 看到值 V( W(r))。每次读取看到的值由内存模型决定。给出的程序顺序必须反映根据 P 的线程内语义执行操作的程序顺序。
执行发生在一致之前(第 17.4.6 节)。
执行遵循同步顺序一致性。对于 A 中的所有 volatile 读 r,情况不是 so(r, W(r)) 也不是存在写赢 A 使得 wv = rv 和 so(W(r), w) 和 so( w, r)。
Useful Link : What do we really know about non-blocking concurrency in Java?
有用的链接:我们对 Java 中的非阻塞并发真正了解多少?
回答by Vineet Reynolds
If a variable is non-volatile, then the compiler and the CPU, may re-order instructions freely as they see fit, in order to optimize for performance.
如果变量是非易失性的,那么编译器和 CPU 可以按照他们认为合适的方式自由地重新排序指令,以优化性能。
If the variable is now declared volatile, then the compiler no longer attempts to optimize accesses (reads and writes) to that variable. It may howevercontinue to optimize access for other variables.
如果变量现在声明为 volatile,则编译器不再尝试优化对该变量的访问(读取和写入)。然而,它可能会继续优化对其他变量的访问。
At runtime, when a volatile variable is accessed, the JVM generates appropriate memory barrier instructions to the CPU. The memory barrier serves the same purpose - the CPU is also prevent from re-ordering instructions.
在运行时,当访问易失性变量时,JVM 会向 CPU 生成适当的内存屏障指令。内存屏障有同样的目的——CPU 也被阻止重新排序指令。
When a volatile variable is written to (by thread A), all writes to any other variable are completed (or will atleast appear to be) and made visible to A before the write to the volatile variable; this is often due to a memory-write barrier instruction. Likewise, any reads on other variables, will be completed (or will appear to be) before the read (by thread B); this is often due to a memory-read barrier instruction. This ordering of instructions that is enforced by the barrier(s), will mean that all writes visible to A, will be visible B. This however, does not mean that any re-ordering of instructions has nothappened (the compiler may have performed re-ordering for other instructions); it simply means that if any writes visible to A have occurred, it would be visible to B. In simpler terms, it means that strict-program order is not maintained.
当一个 volatile 变量被写入(由线程 A)时,对任何其他变量的所有写入都已完成(或至少看起来是)并且在写入 volatile 变量之前对 A 可见;这通常是由于内存写入屏障指令。同样,对其他变量的任何读取都将在读取之前完成(或看起来是)(由线程 B);这通常是由于内存读取屏障指令。这种由屏障强制执行的指令排序将意味着所有对 A 可见的写操作对 B 可见。 然而,这并不意味着任何指令的重新排序没有发生了(编译器可能已经为其他指令执行了重新排序);它只是意味着,如果发生了对 A 可见的任何写入,那么对 B 也是可见的。简单来说,这意味着不维护严格的程序顺序。
I will point to this writeup on Memory Barriers and JVM Concurrency, if you want to understand how the JVM issues memory barrier instructions, in finer detail.
如果您想更详细地了解 JVM 如何发出内存屏障指令,我将指向有关Memory Barriers 和 JVM Concurrency 的这篇文章。
Related questions
相关问题
回答by Bohemian
Threads are allowed to cache variable values that other threads may have since updated since they read them. The volatile
keyword forces all threads to not cache values.
允许线程缓存其他线程自读取它们以来可能已更新的变量值。该volatile
关键字强制所有线程不缓存值。
回答by Pa?lo Ebermann
This is simply an additional bonus the memory model gives you, if you work with volatile variables.
如果您使用易失性变量,这只是内存模型为您提供的额外奖励。
Normally (i.e. in the absence of volatile variables and synchronization), the VM can make variables from one thread visible to other threads in any order it wants, or not at all. E.g. the reading thread could read some mixture of earlier versions of another threads variable assignments. This is caused by the threads being maybe run on different CPUs with their own caches, which are only sometimes copied to the "main memory", and additionally by code reordering for optimization purposes.
通常(即在没有 volatile 变量和同步的情况下),VM 可以按照它想要的任何顺序使来自一个线程的变量对其他线程可见,或者根本不可见。例如,读取线程可以读取另一个线程变量分配的早期版本的一些混合。这是由于线程可能运行在具有自己的缓存的不同 CPU 上,这些缓存仅有时被复制到“主内存”,此外还由于出于优化目的而重新排序代码。
If you used a volatile variable, as soon as thread B read some value X from it, the VM makes sure that anything which thread A has written before it wrote X is also visible to B. (And also everything which A got guaranteed as visible, transitively).
如果你使用了一个 volatile 变量,只要线程 B 从中读取了一些值 X,VM 就会确保线程 A 在写入 X 之前写入的任何内容对 B 也是可见的。(以及 A 保证为可见的所有内容,传递)。
Similar guarantees are given for synchronized blocks and other types of locks.
为同步块和其他类型的锁提供了类似的保证。