Bidirectional memory barriers
under existing Java semantics

DO NOT USE!

The technique described here is inefficient and will not work once the Java memory model is revised. It is described here as a historical artifact.

The rules for a synchronized block are that none of the actions inside the synchronized block can be performed before the lock is obtained or after it is released. However, actions before the synchronized block do not have to be completed before the lock is obtained. Similarly, actions after a synchronized block can be performed before the lock is released.

In essence, a synchronized block is like a roach motel: statements can move in, but they can't move out.

Initially
x = y = 0; a = new Object(); b = new Object();
Thread 1 Thread 2
synchronized(a) {
  x = 1;
  } 
  y = 1;
synchronized(b) {
  j = y;
  }
  i = x;
Can result in i = 0 and j = 1

A compiler could legally transform the above code into:

Initially
x = y = 0; a = new Object(); b = new Object();
Thread 1 Thread 2
synchronized(a) {
  y = 1;
  x = 1;
  } 
synchronized(b) {
  i = x;
  j = y;
  }
Can result in i = 0 and j = 1

However, if each thread contains two synchronized blocks, the under the current semantics, but not under the proposed semantics, all actions in the first block must be performed before any actions in the second block:

DO NOT USE
Initially
x = y = 0; a = new Object(); b = new Object();
Thread 1 Thread 2
synchronized(a) {
  x = 1;
  }
synchronized(a) {
  y = 1;
  }
synchronized(b) {
  j = y;
  }
synchronized(b) {
  i = x;
  }
Must not result in in i = 0 and j = 1

The proposed semantics for eliminating ''useless'' synchronization would allow the compiler to transform the above program to the following, eliminating the bidirectional memory barrier. In addition, under the proposed semantics, since the threads are locking separate objects, the locks have no effect on the visibility of the memory actions to the other thread.

Initially
x = y = 0; a = new Object(); b = new Object();
Thread 1 Thread 2
synchronized(a) {
  y = 1;
  x = 1;
  }
synchronized(b) {
  i = x;
  j = y;
  }
Can result in in i = 0 and j = 1

Bidirectional memory barriers can't be used to fix double-checked locking
DO NOT USE!

The above discussion suggests the following technique might make double-checked locking work:

// (Still) Broken multithreaded version
// "Double-Checked Locking" idiom
// DO NOT USE
class Foo {
  private Helper helper = null;
  public Helper getHelper() {
    if (helper == null) {
      Helper h;
      synchronized(this) {
        h = helper;
        if (h == null)
          h = new Helper();
        } // force bidirectional memory barrier
          // will not work under proposed semantics
       synchronized(this) {
         helper = h;
	 }
      }
    return helper;
    }
  // other functions and members...
  }
// DO NOT USE

Even under the current semantics, this does not work. The reason is subtle, but has to do with the fact that a thread that sees a non-null value for the helper field might not see the correct values for the fields of the helper object.

This is explained in more detail in the Double-Checked Locking declaration. As a another point to look at is the C++ version with explicit memory barriers; The C++ code has bidirectional memory barriers in two locations. To make the Java version work, you would need to use the technique described on this page to implement both memory barriers. Since that would mean that each invocation of getHelper enters at least two synchronized regions, it would be much slower than just synchronizing the getHelper method.