How to create a Singleton class properly in Java
A Singleton class is a class of which only one instance exists. There is a need for such a design pattern for loggers, database connections and other scenarios.
The main challenge comes when you want to implement a Singleton in a mutli-threaded environment and ensure that all the threads are synchronised when creating the Singleton instance.
Here are the key points:
- We'll use the volatile keyword to mark the instance variable and ensure it's thread-safe. Volatile means that every thread should have consistent knowledge of the variable's value and read it from memory and not from the CPU cache. This is critical unless you run your program on a single processor which is very unlikely.
private static volatile SingletonTest instance = null;
- To avoid multiple threads creating an instance at the same time, we'll use the synchronized keyword to synchronise the code for creating the instance. The key here is where exactly to put the synchronisation to ensure minimum performance penalty and maximum protection.
- Double-checking locking mechanism will be used for creating the initial instance and to avoid any chance of two instances created at the same time while again ensuring best possible performance.
- The getInstance method will be static and it will use lazy initialisation. This means that only when invoked, it will create a new instance in case there is none yet created (it's null) and not beforehand.
public static SingletonTest getInstance() {
if (instance == null) {
synchronized (SingletonTest.class) {
if (instance == null) {
instance = new SingletonTest();
}
}
}
return instance;
}
- The constructor will be marked as private and we'll also add protection against reflection being used to create a new instance like this:
private SingletonTest() {
if (instance != null) {
throw new RuntimeException("Use getinstance() to create a new instance");
}
}
So the full code looks like this:
public class SingletonTest {
private static volatile SingletonTest instance = null;
private SingletonTest() {
if (instance != null) {
throw new RuntimeException("Use getinstance() to create a new instance");
}
}
public static SingletonTest getInstance() {
if (instance == null) {
synchronized (SingletonTest.class) {
if (instance == null) {
instance = new SingletonTest();
}
}
}
return instance;
}
}
Now to start using this SingletonTest you have to get an instance like this:
SingletonTest test = SingletonTest.getInstance();
This is how you can create properly a Singleton class in Java following the best practices for maximum protection and minimal performance penalty using synchronisation and volatile keywords.