【问题标题】:Synchronization between multiple volatile variables多个 volatile 变量之间的同步
【发布时间】:2019-05-06 06:29:09
【问题描述】:

给定以下代码:

public class Test {

  private volatile boolean a;

  private volatile boolean b;

  private void one () {
    a = true;
    System.out.println (b);
  }

  private void two () {
    b = true;
    System.out.println (a);
  }

  public static void main (String[] args) throws Exception {
    Test s = new Test ();
    Thread one = new Thread (s::one);
    Thread two = new Thread (s::two);
    one.start ();
    two.start ();
    one.join ();
    two.join ();
  }

}

是否保证(在 Java 内存模型下)至少一个线程打印true

我知道在对 volatile 变量的写入和看到更新值的读取之间存在发生前的关系,但我似乎没有一个线程可以看到更新的值,尽管我不能不要让它发生。

【问题讨论】:

  • 我认为它必须至少打印一个true,尽管我实际上不会编写这样的代码。如果one打印false,那么它必须已经执行a=true;,所以另一个线程必须打印true。如果两个线程交错,它们都可以打印true,但我没有看到一条可以同时打印false 的路径。
  • 我没有看到这里的冲突。两者都没有办法被锁定,因为 volatile 变量只会被更新和释放,例如一旦 b = true 完成,它会释放 b 上的锁,而不是在等待 a 被释放时保留它
  • 我想你的问题可能会得到解答here
  • 这不是捎带。我认为这里的症结在于 println 是否可以在 volatile 的分配之上重新排序,我认为答案是“不”。
  • 这就是我要说的。问题是,程序顺序是有保证的,并且只允许不违反程序顺序的相同结果的重新排序。由于在这里重新排序会产生不同的结果,我认为这是不允许的。不过,我很想在内存模型中找到更明确的规则。 @AndyTurner

标签: java concurrency volatile


【解决方案1】:

是的,这是有保证的。

为了证明这一点,假设不失一般性,线程 1 打印 false。由于b 是易失的,这意味着线程1 在线程2 写入b 之前执行了打印。但如果是这种情况,那么当线程 2 执行它自己的打印时,a 必须已经被线程 1 设置为 true

请注意,根据JLS §17.4.5,在写入之前无法重新排序打印:

  • 如果 xy 是同一线程的操作,并且 x 在程序顺序中位于 y 之前,然后 hb(x, y) [x 发生在 y] 之前。

此外,对ab 的写入将立即对其他线程可见:

  • volatile 字段 (§8.3.1.4) 的写入发生在对该字段的每次后续读取之前。

【讨论】:

  • 你能澄清一下 17.4.5 的哪一点说它们不能重新排序吗?
  • @AndyTurner 刚刚添加了一个报价。
  • “对 volatile 字段的写入(第 8.3.1.4 节)发生在对该字段的每次后续读取之前。”这并没有说明在读取另一个字段之前发生的对字段的写入。
  • 同样在 17.4.5 中:“应该注意的是,两个动作之间存在发生前的关系并不一定意味着它们必须在实现中按该顺序发生。”
  • “如果 x 和 y 是同一个线程的动作,并且 x 在程序顺序中位于 y 之前,则 hb(x, y) [x 发生在 y 之前]。”我想这就是我缺少的部分。
猜你喜欢
  • 2013-08-10
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-24
  • 1970-01-01
  • 2023-01-26
  • 1970-01-01
相关资源
最近更新 更多