user1071840
user1071840

Reputation: 3592

Why doesn't double checked locking fail like this in case of Singleton classes?

1.   class Foo {
2.       private Helper helper = null;
3.       public Helper getHelper() {
4.           if (helper == null) {
5.              synchronized(this) {
6.                 if (helper == null) {
7.                    helper = new Helper();
8.                 }
9.              }
10.          }
11.          return helper;
12.      }
13.  }

The reason why this structure is considered broken is generally described the reordering of assignments done by the compiler such that the Helper constructor is called after the write to the helper variable. My question is that how is this code thread-safe and are the following steps possible?

I don't see how this solution is any better than single checked locking.

Upvotes: 1

Views: 104

Answers (3)

John Vint
John Vint

Reputation: 40256

It's still broken because the writes of any fields within Helper may not be published even though the Helper instance is not-null. For instance:

class Helper {
   int someField;
   public Helper(){
     someField = 10;
   }
}

In this case, it is possible according to the JLS to have something like:

Helper someHelper = getHelper();
if(someHelper.someField == 0){
   // error
}

The line marked //error in theory could be hit since the read & write of someField aren't synchronized with the write of helper within getHelper().

Upvotes: 0

Durandal
Durandal

Reputation: 20059

This works for the reference to the helper, but is still subtly broken.

Its broken because the VM is allowed to reorder program actions within the synchronized block as much as it likes, so the reference helper can be set to non-null before construction of the helper instance (by Thread 1) has completed.

Thread 2 can now see not-null outside the synchronized block, never attempts to enter the synchronized block (Thread 1 would be still holding the lock and be busy constructing Helper) and works with the half-constructed Helper instance.

This may or may not happen on a specific VM version. But the specification explicitly allows a VM to do this. Thats why the example is broken. It could be fixed by declaring helper volatile (only with Java 5+).

Upvotes: 4

Kayaman
Kayaman

Reputation: 73558

How could thread 1 give up the monitor after checking that helper is null? It won't release the lock until it has initialized helper.

This didn't work years ago on the JVM, but they changed the memory model and it fixed this.

The current "best" way is not DCL, but to implement singletons as enums.

Upvotes: 2

Related Questions