3

我一直在阅读 Java 并发性,却忘记了使用相同锁的两个线程中的同步块也会影响变量的可见性,即使它们没有被定义为“易失性”。如果我有这样的代码

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 中可见,因为它在同步块内。

4

2 回答 2

3

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

不,Java 内存模型是根据“发生在”关系定义的:

两个动作可以通过happens-before关系排序。如果一个动作发生在另一个动作之前,那么第一个动作对第二个动作可见并在第二个动作之前排序。

规范继续说:

如果动作 x 与后续动作 y 同步,那么我们也有 hb(x, y)。

wherehb代表发生在之前,和

监视器 m 上的解锁操作与 m 上的所有后续锁定操作同步(其中“后续”根据同步顺序定义)。

另请注意:

如果 hb(x, y) 和 hb(y, z),则 hb(x, z)。

因此,在您的示例中,synchronized(lock)aroundb将为以下读取建立发生前的关系,因此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 中可见,因为它在同步块内。

是的,见上文。

于 2013-01-15T00:18:48.273 回答
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;
      }
    }
  }
}
于 2013-01-15T00:15:19.117 回答