同步和可见范围

时间:2013-01-15 00:06:36

标签: java concurrency synchronized java-memory-model

我一直在阅读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从等式中删除怎么办?我想知道是否只保证在threadTwo中可见b,因为它在同步块内。

2 个答案:

答案 0 :(得分:3)

  

是否保证代码会突破while循环?

没有。 Java内存模型是根据“happens before”关系定义的:

  

可以通过先发生关系来排序两个动作。如果一个动作发生在另一个动作之前,则第一个动作在第二个动作之前可见并且在第二个之前被命令。

规范继续说:

  

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

其中hb代表之前发生的事情,

  

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

另请注意:

  

如果是hb(x,y)和hb(y,z),那么hb(x,z)。

因此,在您的示例中,synchronized(lock)周围的b将为以下读取建立一个before-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怎么办?我想知道是否只保证在threadTwo中可见b,因为它在同步块内。

是的,见上文。

答案 1 :(得分:0)

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

  

是否保证代码会突破while循环?

在实践中,是的。锁只能在短时间内保持在threadOne方法中设置b。 threadTwo中有足够的上下文切换,以便threadOne能够执行同步块。 “在实践中”意思是:在极不可能的情况下(以及实现不当的JVM线程),threadOne可能会被保留在同步块之外,而threadTwo继续为if检查重新获取同步锁。 / em>(事实上,我挑战任何人来制作OPs场景未完成的工作示例。)

  

如果我们从等式中删除变量c怎么办?我想知道是否   保证在threadTwo中只能看到b,因为它在里面   同步块。

相同。在实践中,是的。

要获得额外的功劳(挑战),找一个执行以下代码的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;
      }
    }
  }
}