views:

663

answers:

5

Hi All,

I happened upon an article recently discussing the double checked locking pattern in Java and it's pitfalls and now I'm wondering if a variant of that pattern that I've been using for years now is subject to any issues.

I've looked at many posts and articles on the subject and understand the potential issues with getting a reference to a partially constructed object, and as far as I can tell, I dont think my implementation is subject to these issues. Does anyone see any issues with the following pattern?

And, if not, why dont people use it? I've never seen it recommended in any of the disucssion I've seen around this issue.

public class Test {
    private static Test instance;
    private static boolean initialized = false;

    public static Test getInstance() {
        if (!initialized) {
            synchronized (Test.class) {
                if (!initialized) {
                    instance = new Test();
                    initialized = true;
                }
            }
        }
        return instance;
    }
}
+7  A: 

That would work if initialized was volatile. Just as with synchronized the interesting effects of volatile are not really so much to do with the reference as what we can say about other data. Setting up of the instance field and the Test object is forced to happen-before the write to initialized. When using the cached value through the short circuit, the initialize read happens-before reading of instance and objects reached through the reference. There is no significant difference in having a separate initialized flag (other than it causes even more complexity in the code).

(The rules for final fields in constructors for unsafe publication are a little different.)

However, you should rarely see the bug in this case. The chances of getting into trouble when using for the first time is minimal, and it is a non-repeated race.

The code is over-complicated. You could just write it as:

private static final Test instance = new Test();

public static Test getInstance() {
    return instance;
}
Tom Hawtin - tackline
But beware of creating the world on application startup. Lazy initialisation has its uses.
rsp
@rsp This is still lazy : while you don't access the class, it won't initialize. But it true that, is some case, we want the laziness to be really at the last moment...
KLE
@Tom - there's a typo on your return value
McDowell
McDowell: I elided my `Test()` method. :) KLE: If you have such requirements about initialisation, I think the system you are working on has severe robustness issues.
Tom Hawtin - tackline
Considering how rarely lazy initialization is actually necessary, and how much more rarely still class-level laziness is not sufficient, I just can't fathom how anyone could justify the mental work going into all these discussions about double-checked locking and whatnot.
Michael Borgwardt
Michael: People love crazy optimisation! It does you good to think about. For some purposes (say implementing `java.util.concurrent`) then it is very useful. Almost always, simple code is best for keepers.
Tom Hawtin - tackline
+7  A: 

Double check locking is broken. Since initialized is a primitive, it may not require it to be volatile to work, however nothing prevents initialized being seen as true to the non-syncronized code before instance is initialized.

EDIT: To clarify the above answer, the original question asked about using a boolean to control the double check locking. Without the solutions in the link above, it will not work. You could double check lock actually setting a boolean, but you still have issues about instruction reordering when it comes to creating the class instance. The suggested solution does not work because instance may not be initialized after you see the initialized boolean as true in the non-syncronized block.

The proper solution to double-check locking is to either use volatile (on the instance field) and forget about the initialized boolean, and be sure to be using JDK 1.5 or greater, or initialize it in a final field, as elaborated in the linked article and Tom's answer, or just don't use it.

Certainly the whole concept seems like a huge premature optimization unless you know you are going to get a ton of thread contention on getting this Singleton, or you have profiled the application and have seen this to be a hot spot.

Yishai
Double-checked locking *was* broken. Don't forget to read the section entitled "Under the new Java memory model" at the end of your linked document, which shows how and why it works in JDKs 1.5 and above (i.e. over five years now).
Andrzej Doyle
@dtsazza, yes it could be fixed with volatile now, but the OP didn't have that, it was trying to solve it with a boolean.
Yishai
+3  A: 

Double checked locking is indeed broken, and the solution to the problem is actually simpler to implement code-wise than this idiom - just use a static initializer.

public class Test {
    private static final Test instance = createInstance();

    private static Test createInstance() {
        // construction logic goes here...
        return new Test();
    }

    public static Test getInstance() {
        return instance;
    }
}

A static initializer is guaranteed to be executed the first time that the JVM loads the class, and before the class reference can be returned to any thread - making it inherently threadsafe.

matt b
Your example is incorrect because *'instance'* field is not marked as *'final'*, so, it's not guaranteed that there is no thread that can see *'null'* value there.
denis.zhdanov
I don't think that is true based on http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44557 and http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44630 - the final modifier only seems to affect the order of which fields are initialized. However this field should be final anyway in his usage, so I've updated the code regardless.
matt b
A: 

You should probably use the atomic data types in java.util.concurrent.atomic.

Seun Osewa
A: 

reason why double checked locking is broken

synchronize guarantees, that only one thread can enter a block of code. But it doesn't guarantee, that variables modifications done within synchronized section will be visible to other threads. Only the threads that enters the synchronized block is guaranteed to see the changes. This is the reason why double checked locking is broken - it is not synchronized on reader's side. Reading thread may see, that singleton is not null, but singleton data may not be fully initialized (visible).

Ordering is provided by volatile. volatile guarantees ordering, for instance write to volatile singleton static field guaranties that writes to the singleton object will be finished before the write to volatile static field. It doesn't prevent creation singleton of two objects, this is provided by synchronize Class final static fields doesn't need to be volatile, In Java JVM takes care of this problem.

see my post here illustrating example of Singleton with respect to Double-checked locking that looks Clever but broken http://stackoverflow.com/questions/2482092/singleton-pattern-and-broken-double-checked-locking-in-real-world-java-applicatio/3516238#3516238

Sudhakar Kalmari