Singleton Design Pattern
Overview
Singleton pattern is a creational design pattern which ensure that a class has only one instance throughout application and which can be access via global access point.
There are many ways to create Singleton Design pattern. Each way has its own benefit and drawbacks. From the definition, although It seems simple, but muti-threaded environment add more complexities to it. Below are the different ways of creating the Singleton pattern in Java:
- Eager initialization
- Static block initialization
- Lazy initialization
- ThreadSafe Lazy initialization
- ThreadSafe Double check Lazy initialization
- Bill Pugh Singleton
- Enum Singleton
Singleton pattern using Eager initialization
This is the quite basic implementation of Singleton patter. Here we are initializing the instance at the time of loading of class itself.
1public class EagerInitializationSingleton {
2
3 private static EagerInitializationSingleton instance = new EagerInitializationSingleton();
4
5 private EagerInitializationSingleton() {
6 }
7
8 public static EagerInitializationSingleton getInstance() {
9 return instance;
10 }
11}
Drawbacks:
- Object will be created and consume memory even nobody is using this.
- Multiple objects can be created using Serialization
- Multiple objects can be created using Reflection
Singleton pattern using Static block initialization
It's the alternative implementation of Eager initialization
, instead of initializing the static variable at the time of
declaration, We are initializing it in static block.
1
2public class StaticBlockInitializationSingleton {
3
4 private static StaticBlockInitializationSingleton instance;
5
6 static {
7 try {
8 instance = new StaticBlockInitializationSingleton();
9 } catch (Exception e) {
10 System.out.println(e.getCause());
11 }
12 }
13
14 private StaticBlockInitializationSingleton() {
15 }
16
17 public static StaticBlockInitializationSingleton getInstance() {
18 return instance;
19 }
20}
Drawbacks:
- Object will be created and consume memory even nobody is using this.
- Multiple objects can be created using Serialization
- Multiple objects can be created using Reflection
Singleton pattern using Lazy initialization
Lazy initialization implementation help us creating the object at the time of first use. So, if nobody wants this object, object will never be created.
1public class LazyInitializationSingleton {
2
3 private static LazyInitializationSingleton instance;
4
5 private LazyInitializationSingleton() {
6 }
7
8 public static LazyInitializationSingleton getInstance() {
9 if(instance == null)
10 instance = new LazyInitializationSingleton();
11 return instance;
12 }
13}
Drawbacks:
- Multiple object can be created in case of multi-threaded environment. e.g. if two thread simultaneously call
getInstance()
, both will see instance as null and create new object. - Multiple objects can be created using Serialization
- Multiple objects can be created using Reflection
Singleton pattern using ThreadSafe Lazy initialization
This is the thread safe implementation of LazyInitializationSingleton
. Here we have added synchronized
which will make sure only one thread will call it at a time.
1public class ThreadSafeLazyInitializationSingleton {
2
3 private static ThreadSafeLazyInitializationSingleton instance;
4
5 private ThreadSafeLazyInitializationSingleton() {
6 }
7
8 public static synchronized ThreadSafeLazyInitializationSingleton getInstance() {
9 if(instance == null)
10 instance = new ThreadSafeLazyInitializationSingleton();
11 return instance;
12 }
13}
Drawbacks:
- Although it's thread safe, by this thread safety has a higher cost as you can see
synchronized
is at method level, if object already created, all consecutive call will still pass through synchronized which is expensive as it put a lock on every request and refresh the thread context. - Multiple objects can be created using Serialization
- Multiple objects can be created using Reflection
Singleton pattern using ThreadSafe Double check Lazy initialization
Double check Lazy initialization is further enhancement on previous one. As you can see instance == null
is checked twice, outside synchronized
and inside synchronized
. instance == null
inside synchronized
prevent creation of two instance when two thread simultaneously try to create object.
instance == null
helps in performance when object already created and some client try to get the object, then this first null check will return the object without
going through the synchronized
.
Here Object is backed by volatile
which instruct the JVM not to store the object reference in thread memory by instead flush to main memory and also instruct each thread to look
for the object from main memory. Hence it make sure that changes made by one thread are immediately visible to others.
1public class ThreadSafeDoubleCheckLazyInitializationSingleton {
2
3 private static volatile ThreadSafeDoubleCheckLazyInitializationSingleton instance;
4
5 private ThreadSafeDoubleCheckLazyInitializationSingleton() {
6 }
7
8 public static ThreadSafeDoubleCheckLazyInitializationSingleton getInstance() {
9 if (instance == null) {
10 synchronized (ThreadSafeDoubleCheckLazyInitializationSingleton.class) {
11 if (instance == null) {
12 instance = new ThreadSafeDoubleCheckLazyInitializationSingleton();
13 }
14 }
15 }
16
17 return instance;
18 }
19}
Drawbacks:
- Before java 1.5, JVM has a bug around visibility of changes made by one thread to other thread. Although above example cause no issue since java 1.5 but problematic in earlier version.
- Multiple objects can be created using Serialization
- Multiple objects can be created using Reflection
Singleton pattern using Bill Pugh Singleton
Bill Pugh Singleton provide ultimate solution for the singleton without using any synchronized
or volatile
keyword. It make use of JVM functionality to which make sure to intialize the
static classes or static inner classes before making the class ready to use. So here, object is created inside a staic inner class which will only initialized during first use and once
fully initialize , only then client can use it.
1public class BillPughSingleton {
2 public BillPughSingleton() {
3 }
4
5 public static BillPughSingleton getInstance() {
6 return SingletonHelper.INSTANCE;
7 }
8
9 private static class SingletonHelper {
10 private static final BillPughSingleton INSTANCE = new BillPughSingleton();
11 }
12}
Singleton pattern using Enum Singleton
Enums are another approach to create Singleton Object quite easily and with thread safety.
1public enum EnumSingleton {
2 INSTANCE;
3
4 public static void doSomething(){
5 //do something
6 }
7
8}
How to test
1public class Client {
2 public static void main(String[] args) {
3 //Eager initialization
4 System.out.println("Eager initialization");
5 System.out.println(EagerInitializationSingleton.getInstance());
6 System.out.println(EagerInitializationSingleton.getInstance());
7
8 //Static block initialization
9 System.out.println("StaticBlock initialization");
10 System.out.println(StaticBlockInitializationSingleton.getInstance());
11 System.out.println(StaticBlockInitializationSingleton.getInstance());
12
13 //Lazy initialization
14 System.out.println("Lazy initialization");
15 System.out.println(LazyInitializationSingleton.getInstance());
16 System.out.println(LazyInitializationSingleton.getInstance());
17
18 //ThreadSafe Lazy initialization
19 System.out.println("ThreadSafe Lazy initialization");
20 System.out.println(ThreadSafeLazyInitializationSingleton.getInstance());
21 System.out.println(ThreadSafeLazyInitializationSingleton.getInstance());
22
23 //ThreadSafe Double check Lazy initialization
24 System.out.println("ThreadSafe Double check Lazy initialization");
25 System.out.println(ThreadSafeDoubleCheckLazyInitializationSingleton.getInstance());
26 System.out.println(ThreadSafeDoubleCheckLazyInitializationSingleton.getInstance());
27
28 //Bill Pugh Singleton
29 System.out.println("Bill Pugh Singleton");
30 System.out.println(BillPughSingleton.getInstance());
31 System.out.println(BillPughSingleton.getInstance());
32
33 //Enum Singleton
34 System.out.println("Enum Singleton");
35 System.out.println(EnumSingleton.INSTANCE);
36 System.out.println(EnumSingleton.INSTANCE);
37 }
38}
Output Will be :
1Eager initialization
2com.learn.designpattern.creational.singleton.EagerInitializationSingleton@3f0ee7cb
3com.learn.designpattern.creational.singleton.EagerInitializationSingleton@3f0ee7cb
4
5StaticBlock initialization
6com.learn.designpattern.creational.singleton.StaticBlockInitializationSingleton@7d417077
7com.learn.designpattern.creational.singleton.StaticBlockInitializationSingleton@7d417077
8
9Lazy initialization
10com.learn.designpattern.creational.singleton.LazyInitializationSingleton@35bbe5e8
11com.learn.designpattern.creational.singleton.LazyInitializationSingleton@35bbe5e8
12
13ThreadSafe Lazy initialization
14com.learn.designpattern.creational.singleton.ThreadSafeLazyInitializationSingleton@5a39699c
15com.learn.designpattern.creational.singleton.ThreadSafeLazyInitializationSingleton@5a39699c
16
17ThreadSafe Double check Lazy initialization
18com.learn.designpattern.creational.singleton.ThreadSafeDoubleCheckLazyInitializationSingleton@56cbfb61
19com.learn.designpattern.creational.singleton.ThreadSafeDoubleCheckLazyInitializationSingleton@56cbfb61
20
21Bill Pugh Singleton
22com.learn.designpattern.creational.singleton.BillPughSingleton@129a8472
23com.learn.designpattern.creational.singleton.BillPughSingleton@129a8472
24
25Enum Singleton
26INSTANCE
27INSTANCE
Break Singlton using Reflection
Reflection is the feature of java which is capable of breaking most concept in java e.g. field visibility, methods visibility. below example shows how it breaks the certain Singleton implementation.
1import java.lang.reflect.Constructor;
2
3public class ReflectionSingletonTest {
4
5 public static void main(String[] args) {
6 EagerInitializationSingleton instanceOne = EagerInitializationSingleton.getInstance();
7 EagerInitializationSingleton instanceTwo = null;
8 try {
9 Constructor[] constructors = EagerInitializationSingleton.class.getDeclaredConstructors();
10 for (Constructor constructor : constructors) {
11 //Below code will destroy the singleton pattern
12 constructor.setAccessible(true);
13 instanceTwo = (EagerInitializationSingleton) constructor.newInstance();
14 break;
15 }
16 } catch (Exception e) {
17 e.printStackTrace();
18 }
19 System.out.println(instanceOne.hashCode());
20 System.out.println(instanceTwo.hashCode());
21 }
22
23}
Break Singleton using Serialization
Serialization is another way to break Singleton but luckily we have a way to make singleton Serialization
safe which you can see in next section.
1import java.io.*;
2
3public class SingletonSerializedTest {
4
5 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
6 SerializedSingleton instanceOne = SerializedSingleton.getInstance();
7 ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
8 "filename.ser"));
9 out.writeObject(instanceOne);
10 out.close();
11
12 //deserailize from file to object
13 ObjectInput in = new ObjectInputStream(new FileInputStream(
14 "filename.ser"));
15 SerializedSingleton instanceTwo = (SerializedSingleton) in.readObject();
16 in.close();
17
18 System.out.println("instanceOne hashCode="+instanceOne.hashCode());
19 System.out.println("instanceTwo hashCode="+instanceTwo.hashCode());
20
21 }
22
23}
Serialization Fix for Singleton
1import java.io.Serializable;
2
3public class SerializedSingleton implements Serializable {
4
5 private static final long serialVersionUID = -7604766932017737115L;
6
7 private SerializedSingleton(){}
8
9 private static class SingletonHelper{
10 private static final SerializedSingleton instance = new SerializedSingleton();
11 }
12
13 public static SerializedSingleton getInstance(){
14 return SingletonHelper.instance;
15 }
16
17 protected Object readResolve() {
18 return getInstance();
19 }
20
21}