All About Singleton – Important and Interesting pattern in Java
Edit this problem on GitHub
Introduction
Briefly, Singleton pattern is to ensure a class to have exactly one instance and provide a global point of access to it. There’s a simple graph to give you a big picture of it:
It seems simple enough. Singletons maintain a static reference to the only singleton instance and return a reference to that instance from a static method. However, there are bunch of stuffs we need to take care of in implementing a safe Singleton class. There are two ways (or more) to implement a Singleton. Let’s start with the more complex one.
First Way to Implement Singleton
From the graph above, we could easily get the following:
public class ClassicSingleton { private static ClassicSingleton instance = null; protected ClassicSingleton() { // Exists only to defeat instantiation. } public static ClassicSingleton getInstance() { if(instance == null) { instance = new ClassicSingleton(); } return instance; } }
As you can see, the singleton instance won’t get instantiated until the static method getInstance()
first get called. This technique ensures that singleton instances are created only when needed. In order to prevent client to instantiate ClassicSingleton
instances, the class has to implement a protected constructor.
However, protected constructors can be called by subclasses and by other classes in the same package. Thus, in order for the singleton to work, we cannot use the default package. We have to put the singleton class in an explicit package (different package from all it’s instantiators) so classes in other packages including the default package can’t instantiate singleton instances with something like ClassicSingleton instance = new ClassicSingleton()
Now, is the singleton class ready for use?
NO! It is not thread-safe yet.
Multithreading Situations Of First Implementation
Basically, our code is not thread-safe because of the following code:
if(instance == null) { instance = new Singleton(); }
If a thread is preempted at Line 2 before the assignment is made, the variable instance
will still be null
and another thread can subsequently enter the block and instantiate.
Naturally, you are thinking of synchronize the getInstance()
method:
public synchronized static Singleton getInstance() { if(singleton == null) { singleton = new Singleton(); } return singleton; }
However, since synchronization is very expensive performance-wise (synchronized methods can run up to 100 times slower than unsynchronized methods), we can rewrite the code as follow to enhance the performance:
public static Singleton getInstance() { if(singleton == null) { synchronized(Singleton.class) { if(singleton == null) { singleton = new Singleton(); } } } return singleton; }
This is called Double-checked locking. However, double-checked locking is still not guaranteed to work at this point. Thread 1 can be preempted after the singleton reference has been assigned, but before the singleton is initialized. Since reader threads are not doing any locking and until writer thread comes out of synchronized block, memory will not be synchronized and value of singleton
will not be updated in main memory.
In order for all the threads to know the updated value of singleton
, we should define it as volatile
:
public class ClassicSingleton { private static volatile ClassicSingleton instance = null; protected ClassicSingleton() { // Exists only to defeat instantiation. } public static ClassicSingleton getInstance() { if(singleton == null) { synchronized(ClassicSingleton.class) { if(singleton == null) { singleton = new ClassicSingleton(); } } } return singleton; } }
Or we could have a much simpler implementation if you assume you won’t change your mind to allow multiple singleton instances to be instantiated later on :
public class ClassicSingleton{ private static final ClassicSingleton INSTANCE = new ClassicSingleton(); //to prevent creating another instance of Singleton private ClassicSingleton(){} public static ClassicSingleton getInstance(){ return INSTANCE; } }
Second Way to Implement Singleton
Here comes the simple way to implement singleton: ENUM!!!
public enum EasySingleton{ INSTANCE; }
DONE
The best part is that we fully exploit enum
. We left all the work to JVM and don’t have to worry about thread-safe, serialization, double-checked locking blah blah …..
INSTANCE
above is actually something like:
public static final EasySingleton INSTANCE = new EasySingleton()
Interface and Singleton
A great advantage of using enum
instead of static
is we can consolidate Interface
with enum
:
public enum sampleService implements sampleInterface { INSTANCE; private int variable; @Override public int getVariable() { return variable; } }
With the interface as:
public interface sampleInterface { public int getVariable(); }
Now we can define all the functions in one interface, hide all the implementations in another enum
and expose only the interface to the clients using self-defined get()
functions. SO CLEAN AND NEAT!
public interface CleanInterface { public int getVariable(); public void firstFunction(); public void secondFunction(); public void thirdFunction(); }
public class CleanService { public static final CLASS_NAME = "EXAMPLE"; @Override public String getClassName() { return CLASS_NAME; } public static CleanInterface get() { return CleanInterfaceUtil.INSTANCE; } public enum CleanInterfaceUtil implements CleanInterfaceUtil { INSTANCE; private int variable; @Override public int getVariable() { return variable; } @Override public void firstFunction() { variable++; } @Override public void secondFunction() { variable--; } @Override public void thirdFunction() { } } }
Now you can use all the function exposed from the interface using the following:
CleanInterface exampleService = CleanService.get()
References
Why Enum Singleton are better in Java.
Simply Singleton – Navigate the deceptively simple Singleton pattern