4

As I understand, this is a correct implementation of the double-checked locking pattern in Java (since Java 5):

class Foo {
    private volatile Bar _barInstance;
    public Bar getBar() {
        if (_barInstance == null) {
            synchronized(this) { // or synchronized(someLock)
                if (_barInstance == null) {
                    Bar newInstance = new Bar();
                    // possible additional initialization
                    _barInstance = newInstance;
                }
            }
        }
        return _barInstance;
    }
}

I wonder if absence of volatile is a serious error or just a slight imperfection with possible performance drawback assuming _barInstance accessed only through getBar.

My idea is the following: synchronized introduces happens-before relation. The thread that initializes _barInstance writes its value to the main memory leaving the synchronized block. So there will be no double initialization of _barInstance even when it isn't volatile: other threads have null in theirs local copies of _barInstance (get true in the first check), but have to read the new value from the main memory in the second check after entering the synchronized block (get false and do no re-initialization). So the only problem is an excessive one-per-thread lock acquisition.

As I understand, it's correct in CLR and I believe it's also correct in JVM. Am I right?

Thank you.

Ivan Yurchenko
  • 566
  • 4
  • 16
  • @Todd possibly, but there are only *"```volatile``` is important"* and *"not thread safe"* in answers without explanation. I really want to understand this case in details :) – Ivan Yurchenko Jan 19 '15 at 22:45
  • 3
    Sigh. [Read](http://jeremymanson.blogspot.com/2008/05/double-checked-locking.html) [Jeremy](http://jeremymanson.blogspot.com/2008/11/what-volatile-means-in-java.html) [Manson](http://jeremymanson.blogspot.com/2007/05/double-checked-locking-and-problem-with.html). – David Conrad Jan 19 '15 at 22:50
  • @DavidConrad thank you, probably I understand. JVM can reorder constructor instructions to be performed after the assignment of ```_barInstance``` and the following threads could see ```_barInstance``` as initialized before it actually is. When they see it isn't ```null``` they don't acquire the lock and *happens-before*... well not happens :) And they carry on with not-initialized object. On the other hand, when ```_barInstance``` is ```volatile``` *happens-before* write and reads always holds. Correct? – Ivan Yurchenko Jan 19 '15 at 23:24

1 Answers1

7

Not using volatile may result in errors in the following case:

  • Thread 1 enters getBar() and finds _barInstance to be null
  • Thread 1 attempts to create a Bar object and update the reference to _barInstance. Due to certain compiler optimisations, these operations may be done out of order.
  • Meanwhile, thread 2 enters getBar() and sees a non-null _barInstance but might see default values in member fields of the _barInstance object. It essentially sees a partially constructed object but the reference is not null.

The volatile modifier will prohibit a write or read of the variable _barInstance with respect to any previous read or write. Hence, it will make sure that thread 2 will not see a partially constructed object.

For more details: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Gaurav
  • 96
  • 2