繁体   English   中英

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

[英]How can I use the volatile keyword in Java correctly?

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

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

另一个线程使用该对象:

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

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

非常感谢。

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

你应该大多避免使用它。 改为使用AtomicReference (或适当的其他原子类)。 记忆效应是相同的,意图清晰。

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

留下复杂的技术细节,你可以看到volatile作为变量synchronized修饰符更少或更多。 当您想要同步访问方法或块时,您通常喜欢使用synchronized修饰符,如下所示:

public synchronized void doSomething() {}

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

private volatile SomeObject variable;

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

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

更新 :另见本文

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

声明一个易变的Java变量意味着:

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

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

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

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

  public void stopWork() {
    stop = true;
  }
}

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

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

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

场景1: 假设stop没有被声明为volatile,那么给定的线程会做并“认为”以下内容:

  1. stopWork() :我必须将stop设置为true
  2. 太好了,我在我的本地堆栈中做到了,现在我必须更新JVM的主堆。
  3. 糟糕,JVM告诉我在CPU中给出另一个线程的方法,我必须暂停一段时间......
  4. 好的,我回来了。 现在我可以用我的值更新主堆。 更新......

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

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

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

为什么不在以防万一的情况下声明所有变量volatile 由于Scenario2 / Step3。 它有点低效但仍然比常规同步更好。

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

在缺少volatile或其他发生在之前的关系(例如,在公共锁上的同步)时,任何对assign() myObject写入都不能保证被调用use()的线程看到 - 不是立即,不是及时的,真的不是永远的。

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

你是完全正确的'use'线程可以看到myObject任何'缓存'值,包括它在构造时分配的值和任何中间值(再次没有其他发生前的点)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM