14

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

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

另一个线程使用该对象:

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

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

非常感谢。

4

6 回答 6

11

我试图了解何时使用 volatile 何时不使用

你应该尽量避免使用它。改为使用AtomicReference(或在适当的情况下使用另一个原子类)。记忆效果相同,意图清晰。

我强烈建议阅读优秀的Java Concurrency in Practice以获得更好的理解。

于 2010-06-03T16:51:17.427 回答
6

抛开复杂的技术细节,你可以看到volatileless 或 more 作为variablessynchronized的修饰符。当您想同步对方法或块的访问时,您通常希望使用如下修饰符:synchronized

public synchronized void doSomething() {}

如果您想“同步”对变量的访问,那么您想使用volatile修饰符:

private volatile SomeObject variable;

在幕后他们做了不同的事情,但效果是一样的:下一个访问线程可以立即看到更改。

在您的具体情况下,我认为volatile修饰符没有任何价值。volatile不以任何方式保证分配对象的线程将在使用该对象的线程之前运行。反过来也一样好。您可能只想先在use()方法中执行 nullcheck。

更新:另见这篇文章

对变量的访问就像它被包含在一个同步块中一样,在其自身上同步。我们在第二点中说“表现得好像”,因为至少对程序员(并且可能在大多数 JVM 实现中)没有实际涉及的锁定对象

于 2010-06-03T16:50:12.547 回答
4

声明一个 volatile Java 变量意味着:

  • 这个变量的值永远不会被缓存在线程本地
  • 对变量的访问就像它包含在同步块中一样

volatile 的典型和最常见的用法是

public class StoppableThread extends Thread {
  private volatile boolean stop = false;

  public void run() {
    while (!stop) {
      // do work 
    }
  }

  public void stopWork() {
    stop = true;
  }
}
于 2011-11-13T16:23:39.293 回答
2

在这种情况下,您可以使用 volatile。您将需要围绕对变量的访问或某种类似机制(如 AtomicReference)的 volatile 同步,以保证在分配线程上所做的更改实际上对读取线程是可见的。

于 2010-06-03T17:06:52.803 回答
1

我花了很多时间试图理解volatile关键字。我认为@aleroot给出了世界上最好和最简单的例子。

这反过来又是我对假人的解释(像我一样:-)):

场景 1:假设stop未声明为 volatile,那么给定线程会执行并“认为”以下内容:

  1. stopWork()被称为:我必须设置stoptrue
  2. 太好了,我在本地堆栈中完成了,现在我必须更新 JVM 的主堆。
  3. 糟糕,JVM 告诉我在 CPU 中让路给另一个线程,我必须停止一段时间......
  4. 好的,我回来了。现在我可以用我的值更新主堆。更新中...

场景2:现在让stop声明为 volatile

  1. stopWork()被称为:我必须设置stoptrue
  2. 太好了,我在本地堆栈中完成了,现在我必须更新 JVM 的主堆。
  3. 对不起,伙计们,我现在必须做 (2) - 我被告知是volatile。我要占用CPU的时间长一点...
  4. 更新主堆...
  5. 好的,我完成了。现在我可以屈服了。

没有同步,只是一个简单的想法......

为什么不声明所有变量volatile以防万一?由于 Scenario2/Step3。这有点低效,但仍然比常规同步好。

于 2011-11-18T13:49:17.413 回答
0

这里有一些令人困惑的评论:澄清一下假设两个不同的线程调用assign()use().

在没有volatile,或其他发生前的关系(例如,公共锁上的同步)的情况下,任何写入myObjectinassign()都不能保证被线程调用看到use()- 不是立即,不是及时,甚至永远不会.

是的,volatile这是纠正这个问题的一种方法(假设这是不正确的行为——在某些看似合理的情况下你并不关心这个!)。

您完全正确的是,“使用”线程可以看到 的任何“缓存”值myObject,包括在构造时分配的值和任何中间值(同样在没有其他发生之前的点的情况下)。

于 2010-06-05T09:55:09.557 回答