Java 5 and above only. Assume a multiprocessor shared-memory computer (you're probably using one right now).
Here is a code for lazy initialization of a singleton:
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;
}
}
Does instance
have to be declared volatile
in order to prevent the optimizer from rewriting getInstance() as follows (which would be correct in a sequential program):
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
// instance must be null or we wouldn't be here (WRONG!)
instance = new MySingleton();
}
}
}
Assuming the optimizer does not rewrite the code, if instance
is not declared volatile
is it still guaranteed to be flushed to memory when the synchronized
block is exited, and read from memory when the synchronized
block is entered?
EDIT: I forgot to make getInstance() static. I don't think that changes the validity of the answers; you all knew what I meant.
Yes, instance needs to be volatile using double-checked locking in Java, because otherwise the initialization of MySingleton could expose a partially-constructed object to the rest of the system. It's also true that the threads will sync up when they reach the "synchronized" statement, but that's too late in this case.
Wikipedia and a couple other Stack Overflow questions have good discussions of "double-checked locking", so I'd advise reading up about it. I'd also advise not using it unless profiling shows a real need for performance in this particular code.
There's a safer and more readable idiom for lazy initialization of Java singletons:
If you use the more verbose Double-Checked Locking pattern, you have to declare the field
volatile
, as other have already noted.Yes,
instance
should be declaredvolatile
. Even then, it is advised not to use double-checked locking. It (or to be precise, the Java Memory Model) used to have a serious flaw which permitted publication of partially implemented objects. This has been fixed in Java5, still DCL is an obsolete idiom and there is no need to use it anymore - use the lazy initialization holder idiom instead.From Java Concurrency in Practice, section 16.2:
See http://www.javaworld.com/jw-02-2001/jw-0209-double.html
No. It doesn't have to be volatile. See How to solve the "Double-Checked Locking is Broken" Declaration in Java?
previous attempts all failed because if you can cheat java and avoid volatile/sync, java can cheat you and give you an incorrect view of the object. however the new
final
semantics solves the problem. if you get an object reference (by ordinary read), you can safely read itsfinal
fields.