将 volatile 关键字与可变对象一起使用
在Java中,我知道易失性
关键字提供了变量的可见性。问题是,如果变量是对可变对象的引用,那么 volatile 是否也提供该对象内部成员的可见性?
在下面的示例中,如果多个线程正在访问 易失性可变 m 并更改值,它是否可以正常工作?
例子
class Mutable {
private int value;
public int get()
{
return a;
}
public int set(int value)
{
this.value = value;
}
}
class Test {
public volatile Mutable m;
}
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?
In the example below, does it work correctly if multiple threads are accessing volatile Mutable m
and changing the 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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在您的示例中,
volatile
关键字仅保证任何线程对“m”写入的最后一个引用对于随后读取“m”的任何线程都可见。它不保证有关您的 get() 的任何信息。
因此,使用以下序列:
返回 2 而不是 3 是完全合法的。
volatile
不会对此进行任何更改。但是,如果您将
Mutable
类更改为:那么就保证 Thread-1 中的第二个
get()
将返回 3。但请注意
volatile< /code> 通常不是最好的同步方法。
在简单的获取/设置示例中(我知道这只是一个示例),像 AtomicInteger 这样的类,使用适当的同步并实际提供有用的方法会更好。
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.It doesn't guarantee anything about your get().
So using the following sequence:
it is totally legitimate for you to get back 2 and not 3.
volatile
doesn't change anything to that.But if you change your
Mutable
class to this:Then it is guaranteed that the second
get()
from Thread-1 shall return 3.Note however that
volatile
typically ain't the best synchronization method.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.易失性
仅提供对如此声明的对象的引用的保证。该实例的成员不会同步。根据 维基百科,您有:
所以基本上你所拥有的是,通过声明字段
易失性
,与其交互创建一个“同步点”,之后任何更改都将在其他线程中可见。但此后,使用get()
或set()
是不同步的。 Java 规范有更全面的解释。volatile
only provides guarantees about the reference to the Object that is declared so. The members of that instance don't get synchronized.According to the Wikipedia, you have:
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, usingget()
orset()
is unsynched. The Java Spec has a more thorough explanation.使用
易失性
而不是完全同步
值本质上是一种优化。与同步
访问相比,优化来自于为易失性
值提供的较弱的保证。过早的优化是万恶之源;在这种情况下,邪恶可能很难追踪,因为它会以竞争条件等形式出现。因此,如果您需要询问,您可能不应该使用它。Use of
volatile
rather than a fullysynchronized
value is essentially an optimization. The optimization comes from the weaker guarantees provided for avolatile
value compared with asynchronized
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
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 anysynchronisationsynchronized
locking.As you haven't told us what the "correct" behaviour of your code is, the question cannot be answered.
这是对 volatile 的一些细节的旁注解释。写在这里是因为评论太多了。我想举一些例子来说明 volatile 如何影响可见性,以及它在 jdk 1.5 中如何变化。
给出以下示例代码:
该测试代码的行为在 jdk1.5+ 中定义良好,但在 jdk1.5 之前未定义良好。
在jdk1.5之前的世界中,易失性访问和非易失性访问之间没有定义的关系。因此,该程序的输出可能是:
在 jdk1.5+ 的世界中,易失性的语义发生了变化,因此易失性访问会影响非易失性访问。 - 易失性访问的方式与同步完全相同。因此,在 jdk1.5+ 世界中,只有某些输出是可能的:
输出 3。这是不可能的,因为从易失性 _volN 读取“5”会在 2 个线程之间建立同步点,这意味着在分配给 _volN 之前从 t1 执行的所有操作必须对 t2 可见。
进一步阅读:
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.
Given the following example code:
The behavior of this test code is well defined in jdk1.5+, but is not well defined pre-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:
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:
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 must be visible to t2.
Further reading: