同步和可见性范围

3

我一直在研究Java并发性,但曾经忘记了同步块在两个使用相同锁的线程中也会影响变量的可见性,即使它们没有被定义为“volatile”。如果我的代码像这样:

Object lock = new Object();
boolean a = false, b = false, c = false;

void threadOne() {

   a = true;
   synchronized(lock) {
      b = true;
   }
   c = true;

}

void threadTwo() {

   while (true) {
      synchronized(lock) {
         if (a && b && c) break;
      }
   } 

}

...threadOne和threadTwo将被不同的线程调用:

  1. 这段代码能保证会跳出while循环吗?

  2. 如果我们移除变量c,会发生什么?我想知道是否只有b在threadTwo中是可见的,因为它在同步块内部。

2个回答

3
“代码一定会跳出 while 循环吗?” 不是的。Java 内存模型是基于“happens before”关系定义的: “两个动作可以通过 happens-before 关系进行排序。如果一个动作在另一个动作之前发生,那么第一个动作对第二个动作是可见的并且被排序在其前面。” 规范还指出: “如果操作 x 与后续操作 y 同步,则我们也有 hb(x,y)。” 其中 hb 表示 happens-before,并且 “监视器 m 上的解锁操作与 m 上所有后续锁定操作同步(其中“后续”根据同步顺序定义)。” 另请注意: “如果 hb(x, y) 和 hb(y, z),那么 hb(x, z)。”
所以在你的例子中,synchronized(lock) 包围 b 的操作将会为接下来的读取建立 happens-before 关系,因此 b 的值保证在其他同样使用 synchronized(lock) 的线程中可见。明确地说,
hb(write to b in threadOne, unlock in threadOne) AND 
hb(unlock in threadOne, lock in threadTwo) AND 
hb(lock in threadTwo, read from a in threadTwo) IMPLIES 
hb(write to b in threadOne, read from b in threadTwo) 

同样地,a 将保证对于其他线程是可见的。明确地说,
hb(write to a in threadOne, lock in threadOne) AND 
hb(lock in threadOne, unlock in threadOne) AND 
hb(unlock in threadOne, lock in threadTwo) AND 
hb(lock in threadTwo, read a in threadTwo) IMPLIES 
hb(write to a in threadOne, read a in threadTwo). 

写入和随后读取 c 没有先于关系,因此根据规范,对 c 的写入不一定对 threadTwo 可见。

如果我们将变量c从方程中移除怎么办? 我想知道是否仅保证在同步块中的 b 可以在threadTwo中可见。

是的,请参见上文。

0

假设每个线程都共享您在问题中定义的(不完整)类的同一实例:

代码是否保证会跳出while循环?

实际上是这样的。锁仅在threadOne方法中设置b的短时间内保持。在threadTwo中有足够的上下文切换,以便threadOne能够执行同步块。“实际上”意味着:在极不可能的情况下(和实现不良的JVM线程),threadOne可能被拒绝进入同步块,而threadTwo继续重新获取同步锁进行if检查。(事实上,我挑战任何人提供一个可以完成OPs场景的工作示例)。

如果我们将变量c从等式中移除会怎样?我想知道是否只有b在threadTwo中保证可见,因为它在同步块内。

同样,在实践中是这样的。

对于额外的学分(挑战),请找到执行以下代码的JVM,使其不终止:

public class SyncTest {

  public static void main(String args[]) throws Exception {

          final Shared s = new Shared();
          Thread t1 = new Thread () { public void run() { s.threadOne(); } };
          Thread t2 = new Thread () { public void run() { s.threadTwo(); } };

          t2.start();
          t1.start();
  }
}

class Shared {

  Object lock = new Object();
  boolean a = false, b = false, c = false;

  void threadOne() {
    a = true;
    synchronized(lock) {
       b = true;
    }
    c = true;
  }

  void threadTwo() {
    while (true) {
      synchronized(lock) {
         if (a && b && c) break;
      }
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接