是否有必要使这个变量变得易变?

时间:2013-03-26 22:34:01

标签: java multithreading volatile

我正在经历一场“JAX London 2011”presentation on "Modern Java Concurrency"。在持续时间43:20 - 43:40之间,来自观众的人说下面代码中的shutdown变量应该被声明为volatile并且演示者同意它(并说它之前也被指出,但他们只是没有修改演示文稿)。有问题的代码是:

public abstract class QueueReaderTask implements Runnable {

  private boolean shutdown = false;
  protected BlockingQueue<WorkUnit<String>> lbq;

  public void run() {
    while (!shutdown) {
      try {
        WorkUnit<String> wu = lbq.poll(10, TimeUnit.MILLISECONDS);
        if (wu != null) { doAction(wu.getWork()); }
      } catch (InterruptedException e) {
        shutdown = true;
      }
    }
  }

  public abstract void doAction(String msg);
  public void setQueue(BlockingQueue<WorkUnit<String>> q) { lbq = q; }
}

我的问题: 我不认为shutdown应该被宣布为volatile。 我的理由是shutdownRunnable的成员,每个任务/线程将拥有该变量的不同私有副本。那么,为什么要成为volatile

但是自从JAX 2011中讨论过这个问题以来,我假设在这些受众中有很多专业的Java开发人员。我不认为所有人都会错过这个! 那么,我错过了什么?

P.S: - 我可以理解,如果变量(可能)被多个线程共享,则应该声明变量volatile,如Double-Checked-Locking模式:

class Foo {
        private volatile Helper helper = null;
        public Helper getHelper() {
            if (helper == null) {
                synchronized(this) {
                    if (helper == null)
                        helper = new Helper();
                }
            }
            return helper;
        }
}

1 个答案:

答案 0 :(得分:5)

  

每个任务/线程都将拥有该变量的不同私有副本。那么,为什么要让它“不稳定”?

你是正确的如果只在shutdown实例中修改了QueueReaderTask布尔值。在这种情况下,shutdown仅由一个线程修改,不需要volatile

坦率地说,代码看起来很奇怪。为什么要捕获InterruptedException,设置shutdown布尔值,然后循环并退出。为什么现在只做以下事情?为什么要有shutdown标志?

while (true) {
  try {
    WorkUnit<String> wu = lbq.poll(10, TimeUnit.MILLISECONDS);
    if (wu != null) { doAction(wu.getWork()); }
  } catch (InterruptedException e) {
     Thread.currentThread().interrupt();
     return;
  }
}

也许在帖子中删除了额外的代码?如果没有,我想知道这是否是复制并粘贴在代码的较大部分,其中shutdown在方法调用中设置为true

  

P.S: - 我可以理解,如果变量(可能)由多个线程共享,则应该声明变量'volatile',如Double-Checked-Locking模式:

右。典型的模式是从另一个线程修改shutdown,该线程告诉线程停止处理。在这种情况下,它必须是volatile