Singleton pattern is a creational design pattern that deals with object creation mechanism. It solves the concern of having multiple objects of a class. It enforces one & only one instance of a class.
Concern in hand:
Application needs one and only one instance of an object.
Lazy initialization
Global access
Intent of Concern:
Ensure a class has only one instance, and provide a global point of access to it.
Encapsulated “just-in-time initialization” or “initialization on first use”.
Represent mathematical singleton concept of sets i.e. one & only one.
State full unique repository of state.
Does not allow sub classing of exposed operation of object creation
Practical Examples
Every person is a singleton as person is unique.
House probably has just one central electrical fuse box.
Applicability & Example
Application needs to have a single instance of logger. Single logger instance is shared by all the application users.
Print spooler can be made a singleton to avoid multiple concurrent accesses and creating deadlock. Thus device driver loading
Configuration Files – properties files, xml files etc
Cache as a singleton object as it can have a global point of reference and for all future calls to the cache object the client application will use the in-memory object.
Thread pools
Implementations
Various different implementation ways
Simple Lazy
Create final Public class
Create class level private static instance of that class.
Create Private constructor of that class so that no direct object of the class is initiated.
Static method of that class that is called without creating an object.
To make it thread safe, mark this method as synchronized.
Check either instance of the class, which we created in step 3 is null. If yes then create and return new instance of the class otherwise return already created instance.
Life Cycle: Initialize during first use – Destroyed at application shutdown.
Pros: lazy
Corns: Only for single threaded environment. If synchronized, applicable for multi-threaded.
public final class SimpleLazyST {
private static SimpleLazyST instance = null;
private SimpleLazyST() {
}
public static synchronized SimpleLazyST getInstance() {
if (instance == null) {
instance = new SimpleLazyST();
} else {
System.out.println("Only One instance of SimpleST Object is allowed! No Two instances can co-exists");
}
return instance;
}
}
Double Check Locking
If there are two threads operating, then there might a timestamp when both threads try to create objects. To prevent this use locking mechanism so that the second thread cannot use this getInstance () method until the first thread has completed the process. This lock mechanism is provided by Double Check locking implementation
# It dedication is towards checking the lock criteria first rather than acquiring a lock.
# This reduces the overhead of acquiring a lock by first testing the locking criterion without actually acquiring the lock. Lock is acquired only if the lock criteria return true.
# Eliminates concurrency issues
public final class DoubleCheckST {
private static DoubleCheckST instance = null;
private DoubleCheckST() {
}
public static DoubleCheckST getInstance() {
if (instance == null) { // single check
synchronized (DoubleCheckST.class) {
if (instance == null) { // double check
instance = new DoubleCheckST();
}
}
} else {
System.out.println("Only One instance of SimpleST Object is allowed! No Two instances can co-exists");
}
return instance;
}
}
# Check that the variable is initialized (without obtaining the lock). If it is initialized, return it immediately.
# Obtain the lock.
# Double-check whether the variable has already been initialized: if another thread acquired the lock first, it may have already done the initialization. If so, return the initialized variable.
# Otherwise, initialize and return the variable.
# Life Cycle: Initialize during first use – Destroyed at application shutdown.
# Pros: lazy- thread safe- high performance as only locking during initialization
# Corns: Only in Java 5 & above – code complex
In the above code snippet in case of multiple threads hitting concurrently and tries to create the new instance. In such situation, they may be three or more threads are waiting on the synchronized block to get access. Since we have used synchronized only one thread will be given access. All the remaining threads which were waiting on the synchronized block will be given access when first thread exits this block. However when the remaining concurrent thread enters the synchronized block they are prevented to enter further due to the double check: null check. Since the first thread has already created an instance no other thread will enter this loop.
All the remaining threads that were not oblige to enter the synchronized block along with the first thread will be blocked at the first null check. This mechanism is called double checked locking and it provides significant performance benefit and also it is cost effective solution.
Double Check & Volatile
Without volatile modifier, it's possible for another thread in Java to see half initialized state of ‘instance’ variable, but with volatile variable guaranteeing happens-before relationship, all the write will happen on volatile instance before any read of instance variable
Early Loading
# Apart from lazy implementation, singleton can be implemented using early loading mechanism.
# Instantiate singleton class at the start & referred to the private static instance field.
# Singleton object is created before it is needed.
# JVM takes care of the static variable initialization and ensures that the process is threading safe and that the instance is created before the threads tries to access it.
# Life Cycle: Initialize during class load – Destroyed at application shutdown.
# Pros: lazy- thread safe- high performance as no locking – instance final
# Corns: in memory from class load even if not required
public final class EarlyLoadingST {
private static final EarlyLoadingST instance = new EarlyLoadingST();
private EarlyLoadingST() {
}
public static EarlyLoadingST getInstance() {
System.out.println("Already Created Instance " + instance);
return instance;
}
}
Enum Implementation
# Valid for Java 5 & above
# Provides inbuilt thread-safety during instance creation as ENUM is thread safe.
# Best way to implement Singleton pattern
# No need of volatile & double checking concepts
# This solves another problem.
# Life Cycle: Initialize during first use – Destroyed at application shutdown.
# Pros: lazy- thread safe- no locking- high performance- supports serialization
# Corns: unable to extend any other class
public enum EnumST {
INSTANCE;
// method to show we got the object :)
public void test() {
System.out.println("Got the instance");
}
}
Initialization on Demand Holder (IODH)
# Is a lazy-loaded singleton.
# Idiom can be implemented in both single-threaded/serial and concurrent environments
# Declare & initialize an instance as private static final under static inner class
# When the class DemandHolderSTis loaded, it goes through initialization of only the static fields. Since it doesn’t have any static variable to initialize, DemandHolderInnerST is not loaded during class loading. So, when it will initialize? Well, it will only loaded when the JVM thinks that DemandHolderInnerST must be executed. And that is only when this inner class has been called from the getInstance() method for the first time. Now, when this inner class is loaded, all its static variables are also loaded. So the instance is also initialized by executing the private constructor of the outer class. As the class initialization phase is guaranteed by the JLS to be serial, i.e., non-concurrent, so no further synchronization is required in the static getInstance() method during loading and initialization.
# Life Cycle: Initialize during first use – Destroyed at application shutdown.
# Pros: lazy- thread safe- no locking- high performance
# Corns: not specific
public class DemandHolderST {
private DemandHolderST() {
}
private static class DemandHolderInnerST {
public static final DemandHolderST INSTANCE = new DemandHolderST();
}
public static DemandHolderST getInstance(){
return DemandHolderInnerST.INSTANCE;
}
}
Spoling Games
# Cloning – Remedy is to overload clone () of object class & throw CloneNotSuportedException
# Serializable - Conventional Singletons when implement Serializable interface, they no longer remain singleton as readObject () always return a new instance. This is avoided by using readResolve() & discarding newly created instance.
# Reflection – Directly creating an instance by reflection. Restrict it by throwing some exception in constructor if it is public.
# Multiple JVM - When copies of the Singleton class run in multiple VMs, an instance is created for each machine. That each VM can hold its own Singleton might seem obvious but, in distributed systems such as those using EJBs, Jini, and RMI, it’s not so simple. Since intermediate layers can hide the distributed technologies, to tell where an object is really instantiated may be difficult. Thus systems with EJB, RMI JINI should avoid singletons that hold state
Comments