0

it is mentioned in the Java Memory Model that: When a thread exits a synchronized block as part of releasing the associated monitor, the JMM requires that the local processor cache be flushed to main memory. Similarly, as part of acquiring the monitor when entering a synchronized block, local caches are invalidated so that subsequent reads will go directly to main memory and not the local cache.

so why in that code I must declare instance as volatile since when the second thread enters the synch block will go directly to main memory ??

public final class MySingleton {
  private static MySingleton instance = null;
  private MySingleton() { } 
  public static MySingleton getInstance() {
    if (instance == null) {
      synchronized (MySingleton.class) {
        if (instance == null) {
          instance = new MySingleton();
        }
      }
    }
    return instance;
  }
}

I mean when another thread enters synch block and make the second check it is supposed to update from main memory as mentioned.

Dorgham
  • 962
  • 1
  • 9
  • 17
  • 1
    Possible duplicated [Java Double Checked Locking](http://stackoverflow.com/q/1625118/651140) – Andrzej Jozwik Mar 13 '12 at 09:37
  • How do you arrive at this code without having read [The "Double-Checked Locking is Broken" Declaration](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)? Everything is explained there in easily understandable terms. – Ringding Mar 13 '12 at 12:06
  • more explanations please ... I see that since threads when entering synch block local caches are invalidated so that subsequent reads will go directly to main memory , so thread B is supposed to update its instance value from main memory without need to be volatile. – Dorgham Mar 13 '12 at 15:09

2 Answers2

4

The race condition is this:

  1. Thread A sees instance == NULL and is running this code instance = new MySingleton();. The write to instance is visible but the writes into MySingleton are not yet.

  2. Thread B sees instance != NULL and starts working on instance.

  3. Thread B is now working on an object whose construction it cannot see.

Making instance volatile solves the problem as the JDK memory specification, as of JDK5, guarantees that writes to non-volatile objects will not be seen out of order with respect to a write to a volatile object. So any thread that sees instance != NULL must see the instance itself.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • 1
    Specifically, the writes done by MySingleton's constructor are not guaranteed to be seen by thread B. – Ringding Mar 13 '12 at 12:03
  • but how thread B can see instance != null while thread A did not release the lock of synch (which means thread A did not flush its cache to main memory) .. so thread B should see the old value of instance = null. – Dorgham Mar 13 '12 at 16:06
  • 1
    The write is not locked in the cache. The cache is always flushing or it would get full and writes from the CPU would stall. Flushing the cache means *ensuring* it has flushed. Not flushing it doesn't glue anything in it. – David Schwartz Mar 13 '12 at 21:01
  • 1
    By the way, the cache involved here on actual hardware is *not* a memory cache. It's the CPU's write posting buffer, read prefetch, and instruction reordering. Real caches are 100% SMP coherent. Java descriptions will typically be about a hypothetical machine that has no cache coherency but no write posting buffers. The real machines Java runs on are not like the imaginary, theoretical machines Java is defined in terms of. So some things not defined by Java as working will always work. And some failure modes in the hypothetical Java machine will fail on real hardware, but differently. – David Schwartz Mar 14 '12 at 00:24
  • so thats why we need volatile to ensure the same ordering of instructions ... nice .. really great answer bro .. thanks a lot for your help. – Dorgham Mar 14 '12 at 11:24
1

You have to declare it volatile otherwise there is no guarantee that two calls to getInstance() will return the same instance.

There is no guarantee that main memory will be accessed, only a cache-consistent value. i.e. all threads will see the same value.

BTW: You know of course its far more complex than needed. All you need is

public enum MySingleton {
     INSTANCE;
}

does much the same thing.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130