Question

I've been reading up on Java concurrency and had forgot the fact that synchronization blocks in two threads using the same lock also affect the visibility of variables, even though they were not defined as "volatile". If I have code like this

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;
      }
   } 

}

... and threadOne and threadTwo will be called by different threads:

  1. Is it guaranteed that the code will break out of the while loop?

  2. What if we remove variable c out of the equation? I'm wondering if only b is guaranteed to be visible in threadTwo because it was inside the synchronization block.

Was it helpful?

Solution

Is it guaranteed that the code will break out of the while loop?

No. The Java memory model is defined in terms of "happens before" relationships:

Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second.

The spec goes on to say:

If an action x synchronizes-with a following action y, then we also have hb(x, y).

where hb stands for happens-before, and

An unlock action on monitor m synchronizes-with all subsequent lock actions on m (where "subsequent" is defined according to the synchronization order).

Also note that:

If hb(x, y) and hb(y, z), then hb(x, z).

So in your example, the synchronized(lock) around b will establish a happens-before relationship for the following read, and thus the value of b is guaranteed to be visible in other threads that also use synchronized(lock). Explicitly,

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) 

Similarly, a will be guaranteed to be visible to the other thread. Explicitly,

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). 

The write and then subsequent read of c does not have a happens-before relationship, so therefore, according to the spec, the write to c is not necessarily visible to threadTwo.

What if we remove variable c out of the equation? I'm wondering if only b is guaranteed to be visible in threadTwo because it was inside the synchronization block.

Yes, see above.

OTHER TIPS

Given the assumption that each thread is sharing the same instance of the (incomplete) class you have defined in your question:

Is it guaranteed that the code will break out of the while loop?

In practice, yes. The lock is only held for a short time to set b in threadOne method. There are enough context switches in threadTwo for threadOne to be able execute the sync block. "In practice" meaning: in an EXTREMELY unlikely scenario (and poorly implemented JVM threading), it may be possible for threadOne to be kept out of the sync block while threadTwo continues to reacquire the sync lock for the if check. (In fact, I challenge anyone to produce a working example where the OPs scenario does not complete).

What if we remove variable c out of the equation? I'm wondering if only b is guaranteed to be visible in threadTwo because it was inside the synchronization block.

Same. In practice, yes.

For extra credit (the challenge), find a JVM that executes the following code, such that it does not terminate:

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;
      }
    }
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top