说我有两个线程和一个对象。一个线程分配对象:
public void assign(MyObject o) {
myObject = o;
}
另一个线程使用该对象:
public void use() {
myObject.use();
}
变量myObject是否必须声明为volatile?我试图了解何时使用volatile而不是什么时候,这让我感到困惑。是否有可能第二个线程在其本地内存缓存中保留对旧对象的引用?如果没有,为什么不呢?
非常感谢。
答案 0 :(得分:11)
我想了解何时使用 易变的,什么时候不
你应该避免使用它。改为使用AtomicReference(或其他atomic类)。记忆效应相同,意图很多更清晰。
我强烈建议您阅读优秀的Java Concurrency in Practice以便更好地理解。
答案 1 :(得分:6)
抛弃复杂的技术细节后,您可以将volatile
更少或更多地视为变量的synchronized
修饰符。如果您想同步对方法或块的访问,那么您通常希望使用synchronized
修饰符,如下所示:
public synchronized void doSomething() {}
如果您想“同步”对变量的访问权限,那么您希望使用volatile
修饰符:
private volatile SomeObject variable;
在幕后,他们做了不同的事情,但效果是相同的:对于下一个访问线程,更改立即可见。
在您的具体情况下,我认为volatile
修饰符没有任何价值。 volatile
不保证分配对象的线程在使用该对象线程之前运行。反过来也可以这样好。您可能只想先在use()
方法中进行nullcheck。
更新:另见this article:
对变量的访问就好像一样,它包含在一个同步块中,并在其自身同步。我们在第二点说“好像”,因为至少对程序员来说(并且可能在大多数JVM实现中)没有涉及实际的锁对象。
答案 2 :(得分:4)
声明一个易变的Java变量意味着:
volatile的典型和最常见用途是:
public class StoppableThread extends Thread {
private volatile boolean stop = false;
public void run() {
while (!stop) {
// do work
}
}
public void stopWork() {
stop = true;
}
}
答案 3 :(得分:2)
在这种情况下你可以使用volatile。您将需要围绕对变量或类似机制(如AtomicReference)的访问进行volatile,同步,以保证对赋值线程所做的更改实际上对读取线程可见。
答案 4 :(得分:1)
我花了很多时间来了解volatile
关键字。
我认为 @aleroot 已经给出了世界上最好,最简单的例子。
这又是我对假人的解释(比如我:-)):
场景1:假设stop
未声明为易失性
给定的线程确实并“认为”以下内容:
stopWork()
被称为:我必须将stop
设置为true
场景2:现在让stop
声明为易失性:
stopWork()
被称为:我必须将stop
设置为true
volatile
。我必须占用CPU更长一点...... 没有同步,只是一个简单的想法...
为什么不以所有方式声明所有变量volatile
?由于Scenario2 / Step3。它有点低效但仍然比常规同步更好。
答案 5 :(得分:0)
这里有一些令人困惑的评论:澄清一下,你的代码是不正确,假设有两个不同的线程调用assign()
和use()
。
如果没有volatile
或其他发生在之前的关系(例如,在公共锁定上进行同步),则myObject
中对assign()
的任何写入都不能保证被调用use()
的线程 - 不是立即,不是及时的,实际上也不是。
是的,volatile
是纠正这种情况的一种方法(假设这是不正确的行为 - 有合理的情况你不关心这个!)。
'use'主题可以看到myObject
的任何'缓存'值,包括在构造时分配的值和任何中间值(在没有其他事件发生之前)点)。