java 对可变对象使用 volatile 关键字
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4614331/
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
Using volatile keyword with mutable object
提问by Hongbo
In Java, I understand that volatile
keyword provides visibility to variables. The question is, if a variable is a reference to a mutable object, does volatile
also provide visibility to the members inside that object?
在 Java 中,我知道volatile
关键字提供了变量的可见性。问题是,如果变量是对可变对象的引用,是否volatile
也提供对该对象内部成员的可见性?
In the example below, does it work correctly if multiple threads are accessing volatile Mutable m
and changing the value
?
在下面的示例中,如果多个线程正在访问volatile Mutable m
和更改value
?
example
例子
class Mutable {
private int value;
public int get()
{
return a;
}
public int set(int value)
{
this.value = value;
}
}
class Test {
public volatile Mutable m;
}
采纳答案by jtahlborn
This is sort of a side note explanation on some of the details of volatile. Writing this here because it is too much for an comment. I want to give some examples which show how volatile affects visibility, and how that changed in jdk 1.5.
这是对 volatile 的一些细节的旁注解释。写在这里是因为评论太多了。我想举一些例子来说明 volatile 如何影响可见性,以及它在 jdk 1.5 中是如何改变的。
Given the following example code:
给出以下示例代码:
public class MyClass
{
private int _n;
private volatile int _volN;
public void setN(int i) {
_n = i;
}
public void setVolN(int i) {
_volN = i;
}
public int getN() {
return _n;
}
public int getVolN() {
return _volN;
}
public static void main() {
final MyClass mc = new MyClass();
Thread t1 = new Thread() {
public void run() {
mc.setN(5);
mc.setVolN(5);
}
};
Thread t2 = new Thread() {
public void run() {
int volN = mc.getVolN();
int n = mc.getN();
System.out.println("Read: " + volN + ", " + n);
}
};
t1.start();
t2.start();
}
}
The behavior of this test code is well defined in jdk1.5+, but is notwell defined pre-jdk1.5.
此测试代码的行为在 jdk1.5+ 中定义良好,但在 jdk1.5之前没有很好定义。
In the pre-jdk1.5 world, there was no defined relationship between volatile accesses and non-volatile accesses. therefore, the output of this program could be:
在 jdk1.5 之前的世界中,易失性访问和非易失性访问之间没有明确的关系。因此,该计划的产出可能是:
- Read: 0, 0
- Read: 0, 5
- Read: 5, 0
- Read: 5, 5
- 读取:0, 0
- 读取:0, 5
- 读取:5, 0
- 阅读:5、5
In the jdk1.5+ world, the semantics of volatile were changed so that volatile accesses affect non-volatile accesses in exactly the same way as synchronization. therefore, only certain outputs are possible in the jdk1.5+ world:
在 jdk1.5+ 世界中,volatile 的语义发生了变化,因此 volatile 访问影响非 volatile 访问的方式与同步完全相同。因此,在 jdk1.5+ 世界中只有某些输出是可能的:
- Read: 0, 0
- Read: 0, 5
- Read: 5, 0 <- not possible
- Read: 5, 5
- 读取:0, 0
- 读取:0, 5
- 读取:5, 0 <- 不可能
- 阅读:5、5
Output 3. is not possible because the reading of "5" from the volatile _volN establishes a synchronization point between the 2 threads, which means all actions from t1 taken before the assignment to _volN mustbe visible to t2.
输出 3. 是不可能的,因为从易失性 _volN 读取“5”在 2 个线程之间建立了一个同步点,这意味着在分配给 _volN 之前从 t1 执行的所有操作必须对 t2 可见。
Further reading:
进一步阅读:
回答by Gugussee
In your example the volatile
keyword only guarantees that the last reference written, by any thread, to 'm' will be visible to any thread reading 'm' subsequently.
在您的示例中,volatile
关键字仅保证由任何线程写入 'm' 的最后一个引用将对随后读取 'm' 的任何线程可见。
It doesn't guarantee anything about your get().
它不保证任何关于您的get()。
So using the following sequence:
所以使用以下顺序:
Thread-1: get() returns 2
Thread-2: set(3)
Thread-1: get()
it is totally legitimate for you to get back 2 and not 3. volatile
doesn't change anything to that.
你得到 2 而不是 3 是完全合法的。 volatile
不会改变任何东西。
But if you change your Mutable
class to this:
但是如果你把你的Mutable
班级改成这样:
class Mutable {
private volatile int value;
public int get()
{
return a;
}
public int set(int value)
{
this.value = value;
}
}
Then it is guaranteed that the second get()
from Thread-1 shall return 3.
然后保证get()
Thread-1中的第二个将返回 3。
Note however that volatile
typically ain't the best synchronization method.
但是请注意,这volatile
通常不是最好的同步方法。
In you simple get/set example (I know it's just an example) a class like AtomicInteger
, using proper synchronization and actually providing useful methods, would be better.
在您简单的获取/设置示例(我知道这只是一个示例)中AtomicInteger
,使用适当的同步并实际提供有用的方法的类会更好。
回答by Mario F
volatile
only provides guarantees about the reference to the Object that is declared so. The members of that instance don't get synchronized.
volatile
仅提供关于对如此声明的 Object 的引用的保证。该实例的成员不会同步。
According to the Wikipedia, you have:
根据维基百科,你有:
- (In all versions of Java) There is a global ordering on the reads and writes to a volatile variable. This implies that every thread accessing a volatile field will read its current value before continuing, instead of (potentially) using a cached value. (However, there is no guarantee about the relative ordering of volatile reads and writes with regular reads and writes, meaning that it's generally not a useful threading construct.)
- (In Java 5 or later) Volatile reads and writes establish a happens-before relationship, much like acquiring and releasing a mutex.
- (在所有 Java 版本中)对 volatile 变量的读取和写入具有全局顺序。这意味着访问 volatile 字段的每个线程将在继续之前读取其当前值,而不是(可能)使用缓存值。(但是,无法保证 volatile 读取和写入与常规读取和写入的相对顺序,这意味着它通常不是有用的线程构造。)
- (在 Java 5 或更高版本中)Volatile 读取和写入建立了一个发生在之前的关系,就像获取和释放互斥锁一样。
So basically what you have is that by declaring the field volatile
, interacting with it creates a "point of synchronization", after which any change will be visible in other threads. But after that, using get()
or set()
is unsynched. The Java Spechas a more thorough explanation.
所以基本上你所拥有的是通过声明 field volatile
,与其交互创建一个“同步点”,之后任何更改都将在其他线程中可见。但在那之后,使用get()
或set()
不同步。在Java规范有一个更透彻的解释。
回答by OrangeDog
volatile
does not "provide visibility". Its only effect is to prevent processor caching of the variable, thus providing a happens-before relation on concurrent reads and writes. It does not affect the members of an object, nor does it provide any synchronisationsynchronized
locking.
volatile
不“提供可见性”。它的唯一作用是防止处理器缓存变量,从而为并发读取和写入提供一个发生在之前的关系。它不影响对象的成员,也不提供任何同步synchronized
锁定。
As you haven't told us what the "correct" behaviour of your code is, the question cannot be answered.
由于您没有告诉我们您的代码的“正确”行为是什么,因此无法回答这个问题。
回答by Raedwald
Use of volatile
rather than a fully synchronized
value is essentially an optimization. The optimization comes from the weaker guarantees provided for a volatile
value compared with a synchronized
access. Premature optimmization is the root of all evil; in this case, the evil could be hard to track down because it would be in the form of race conditions and such like. So if you need to ask, you probably ought not to use it.
使用volatile
而不是完全synchronized
值本质上是一种优化。volatile
与synchronized
访问相比,优化来自为值提供的较弱保证。过早优化是万恶之源;在这种情况下,邪恶可能很难追踪,因为它会以竞争条件等形式存在。所以如果你需要问,你可能不应该使用它。