如何正确使用Java中的volatile关键字?

时间:2010-06-03 16:44:37

标签: java volatile

说我有两个线程和一个对象。一个线程分配对象:

public void assign(MyObject o) {
    myObject = o;
}

另一个线程使用该对象:

public void use() {
    myObject.use();
}

变量myObject是否必须声明为volatile?我试图了解何时使用volatile而不是什么时候,这让我感到困惑。是否有可能第二个线程在其本地内存缓存中保留对旧对象的引用?如果没有,为什么不呢?

非常感谢。

6 个答案:

答案 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未声明为易失性 给定的线程确实并“认为”以下内容:

  1. stopWork()被称为:我必须将stop设置为true
  2. 很好,我在本地堆栈中完成了,现在我必须更新JVM的主堆。
  3. 糟糕,JVM告诉我在CPU中给出另一个线程的方法,我必须暂停一段时间......
  4. 好的,我回来了。现在我可以用我的值更新主堆。正在更新......
  5. 场景2:现在让stop声明为易失性

    1. stopWork()被称为:我必须将stop设置为true
    2. 很好,我在本地堆栈中完成了,现在我必须更新JVM的主堆。
    3. 对不起伙计们,我必须做(2)现在 - 我被告知这是volatile。我必须占用CPU更长一点......
    4. 更新主堆...
    5. 好的,我完成了。现在我可以屈服了。
    6. 没有同步,只是一个简单的想法...

      为什么不以所有方式声明所有变量volatile?由于Scenario2 / Step3。它有点低效但仍然比常规同步更好。

答案 5 :(得分:0)

这里有一些令人困惑的评论:澄清一下,你的代码是不正确,假设有两个不同的线程调用assign()use()

如果没有volatile或其他发生在之前的关系(例如,在公共锁定上进行同步),则myObject中对assign()的任何写入都不能保证被调用use()的线程 - 不是立即,不是及时的,实际上也不是。

是的,volatile是纠正这种情况的一种方法(假设这是不正确的行为 - 有合理的情况你不关心这个!)。

'use'主题可以看到myObject的任何'缓存'值,包括在构造时分配的值和任何中间值(在没有其他事件发生之前)点)。