I have read about many possible ways to create a singleton for the multithreaded environment in Java, like Enums, Double-check locking, etc.
I found a simple way that is also working fine and I unable to find its drawbacks or failure cases. May anyone explain when it may fail or why we should not choose this approach.
public final class MySingleton {
public final static MySingleton INSTANCE = new MySingleton();
private MySingleton(){}
}
I am testing it with the below code and working fine:
public class MyThread {
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) {
Thread thread = new Thread(() -> {
MySingleton singleton = MySingleton.INSTANCE;
System.out.println(singleton.hashCode() + " " + Thread.currentThread().getName());
});
thread.start();
}
}
}
Every comment is appreciated.
Your singleton is instantiated when the class is loaded, therefore when the main method is started, it is already instantiated and from a multithreading perspective is safe.
But there are some arguments why this might not be the best approach:
And additionally using singletons is not a good design as you hardcode the dependency between the client (that uses this class) and the implementation of this class. So it is hard to replace your implementation with a mock for testing.
Yes, this is a fine implementation of a singleton.
The test shows... something; but it doesn't really explain whether it's working or not. What you are trying to show (that only one instance is created) is essentially impossible to show with a test, because it's something that's guaranteed by the language spec.
See JLS 12, in particular JLS 12.4, which describes how classes are initialized.
For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation. The procedure for initializing C is then as follows:
- Synchronize on the initialization lock, LC, for C. This involves waiting until the current thread can acquire LC.
- If the Class object for C indicates that initialization is in progress for C by some other thread, then release LC and block the current thread until informed that the in-progress initialization has completed, at which time repeat this step.
- If the Class object for C indicates that initialization is in progress for C by the current thread, then this must be a recursive request for initialization. Release LC and complete normally.
- If the Class object for C indicates that C has already been initialized, then no further action is required. Release LC and complete normally.
...
So, classes are guaranteed to only be initialized once (if at all), because the initialization is done whilst holding a class-specific lock; the happens-before guarantees of acquiring and releasing that lock mean that the values of final fields are visible; so the INSTANCE field is guaranteed to be initialized once, and thus there is only one instance of MySingleton possible.
Note that your implementation is effectively the same as an enum:
public enum MySingleton {
INSTANCE
}
Enums are really just syntactic sugar for classes. If you decompiled an enum class, it would look something like:
public class MySingleton {
public static final MySingleton INSTANCE = new MySingleton(0);
private MySingleton(int ordinal) { ... }
}
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