【问题标题】:Why is volatile keyword not allowed for local variables?为什么局部变量不允许使用 volatile 关键字?
【发布时间】:2017-02-18 13:26:27
【问题描述】:

考虑sn-p:

如果在主线程中,我在 方法-

中有这个
volatile CountDownLatch latch = new CountDownLatch(3);

new Thread(new ProcessThread("Worker1",latch, 20000)).start();//20 secs
new Thread(new ProcessThread("Worker2",latch, 60000)).start();//60 secs
new Thread(new ProcessThread("Worker3",latch, 40000)).start();//40 secs

我看到volatile 显示为非法修饰符。并且只允许final最终保证初始化安全

public static class ProcessThread implements Runnable {
  final CountDownLatch latch;
  final long workDuration;
  final String name;

  public ProcessThread(String name, CountDownLatch latch, long duration){
      this.name= name;
      this.latch = latch;
      this.workDuration = duration;
  }
}

下面的对象,即new CountDownLatch(3) 是正确构造的,但我还想确保上面的对象分配到的引用latch 保证对其下面的代码可见。

final CountDownLatch latch = new CountDownLatch(3);

上面的代码是否保证初始化,以便latch对下面的代码完全可见,即

new Thread(new ProcessThread("Worker1",latch, 20000)).start();

【问题讨论】:

  • 有点不清楚你在问什么。当你最后说“对下面的代码完全可见,即”你的意思是你引用的代码行,对构造函数的调用? (我意识到这可能很明显。)或者代码构造函数中?
  • @T.J.Crowder 构造函数中的代码肯定是在与构造函数调用相同的线程中执行的......?这是run() 方法,它(可能)在不同的线程中运行。
  • @AndyTurner:当然可以,我并没有提出其他建议。我建议 OP 澄清他的要求,特别是考虑到在本地使用 volatile

标签: java multithreading volatile final


【解决方案1】:

局部变量存在于栈中;当然,当您调用 same 方法两次时,它们的所有局部变量都在各自的堆栈中。

volatile 仅在 多个 线程将写入 相同 内存位置(在堆上)时才有意义。

这对于方法体内的局部变量绝对没有意义!

【讨论】:

  • 我认为 OP 的主要关注点在最后表达出来,在将本地传递给构造函数之前对其进行初始化。可能值得直接解决。
  • 我猜安迪在那里打败了我;但你是对的;我有点忽略了这方面。
  • 不正确。例如,如果您在方法中关闭局部变量的期货回调怎么办?
【解决方案2】:

final 保证初始化安全。

不适用于局部变量:它只会阻止您重新分配该变量。

final CountDownLatch latch = new CountDownLatch(3);

上面的代码是否会保证初始化,以便锁存器对下面的代码完全可见,即

没有。正是这段代码保证了这一点:

public static class ProcessThread implements Runnable {

    final CountDownLatch latch;

    // Plus the assignment in the constructor.

}

final 字段保证在构造函数完成后可见(通常)。来自JLS Sec 17.5

当一个对象的构造函数完成时,它被认为是完全初始化的。只有在对象完全初始化后才能看到对该对象的引用的线程可以保证看到该对象的 final 字段的正确初始化值。

【讨论】:

  • 这意味着当latch传递给构造函数new ProcessThread("Worker1",latch, 20000))时,它也可以为null。
  • 当你 OP 最后说“对下面的代码完全可见,即”他们将 call 引用到构造函数,而不是构造函数。他们是不同的latchs。
  • @ShirgillFarhanAnsari 确定它可以是 null,如果你是这样的话。
  • latch 的值在调用new ProcessThread("Worker1",latch, 20000)) 时可见,因为它在同一个线程中执行。
  • @ShirgillFarhanAnsari 不,它不能为空,因为你刚刚在上面的行中用CountDownLatch latch = new CountDownLatch(3); 初始化了它,记住new ProcessThread 只是在同一个线程上执行。直到您调用 start 方法和对 Thread.start 的调用与线程上的第一条指令同步后才会启动新线程(JLS 17.4.4),因此您无需担心数据可见性问题在这种特殊情况下。
【解决方案3】:

您在本地执行的操作不会受到其他线程的可见性或干扰问题,因此声明局部变量 volatile 没有意义。

【讨论】:

    最近更新 更多