A recent question here had the following code (well, similar to this) to implement a singleton without synchronisation.
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Now, I think understand what this is doing. Since the instance is static final
, it's built long before any threads will call getInstance()
so there's no real need for synchronisation.
Synchronisation would be needed only if two threads tried to call getInstance()
at the same time (and that method did construction on first call rather than at "static final"
time).
My question is therefore basically: why then would you ever prefer lazy construction of the singleton with something like:
public class Singleton {
private Singleton() {}
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
My only thoughts were that using the static final
method may introduce sequencing issue as in the C++ static initialisation order fiasco.
First off, does Java actually have this problem? I know order within a class is fully specified but does it somehow guarantee consistent order between classes (such as with a class loader)?
Secondly, if the order is consistent, why would the lazy construction option ever be advantageous?
The static initialization order fiasco refers to the ambiguity in the order that objects with static storage duration in different translation units are initialized in.
C++ guarantees that variables in a compilation unit (. cpp file) are initialised in order of declaration. For number of compilation units this rule works for each one separately (I mean static variables outside of classes). But, the order of initialization of variables, is undefined across different compilation units.
Initialization blocks run in the same order in which they appear in the program. Instance Initialization blocks are executed whenever the class is initialized and before constructors are invoked. They are typically placed above the constructors within braces.
A Static Initialization Block in Java is a block that runs before the main( ) method in Java. Java does not care if this block is written after the main( ) method or before the main( ) method, it will be executed before the main method( ) regardless.
Now, I think understand what this is doing. Since the instance is static final, it's built long before any threads will call getInstance() so there's no real need for synchronisation.
No. SingletonHolder
class will be loaded only when you invoke SingletonHolder.INSTANCE
for the very first time. final
Object will become visible to other threads only after it is fully constructed. Such lazy initialization is called Initialization on demand holder idiom
.
Now, I think understand what this is doing. Since the instance is static final, it's built long before any threads will call getInstance() so there's no real need for synchronisation.
Not quite. It is built when the SingletonHolder
class is initialized which happens the first time getInstance
is called. The classloader has a separate locking mechanism, but after a class is loaded, no further locking is needed so this scheme does just enough locking to prevent multiple instantiation.
First off, does Java actually have this problem? I know order within a class is fully specified but does it somehow guarantee consistent order between classes (such as with a class loader)?
Java does have a problem where a class initialization cycle can lead to some class observing another class's static finals before they are initialized (technically before all static initializer blocks have run).
Consider
class A {
static final int X = B.Y;
// Call to Math.min defeats constant inlining
static final int Y = Math.min(42, 43);
}
class B {
static final int X = A.Y;
static final int Y = Math.min(42, 43);
}
public class C {
public static void main(String[] argv) {
System.err.println("A.X=" + A.X + ", A.Y=" + A.Y);
System.err.println("B.X=" + B.X + ", B.Y=" + B.Y);
}
}
Running C prints
A.X=42, A.Y=42
B.X=0, B.Y=42
But in the idiom you posted, there is no cycle between the helper and the singleton so there is no reason to prefer lazy initialization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With