1. Overview
It's important for some classes to have exactly one instance. There can be multiple database connections, but there should be only one connection pool. Multiple mappers, but one mapper factory. To ease the usage of class, it should also be accessible by all parts of the application.
In this tutorial, we're going to investigate the Singleton pattern.
2. When to Use Singleton?
Let's first look at when we should use the Singleton pattern.
Firstly, we can use the Singleton pattern when there must be exactly one instance of a class, and it must be accessible to from a well-known access point.
Secondly, we should use it when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code.
3. How to Implement Singleton
We can categorize the Singleton implementations mainly into two groups. The first category is about initialization type: eager or lazy. Then the second category is about the access type: public static field or public static method.
We'll now provide several implementations to cover these categories.
3.1. Eager Initialization, Public Member, Thread-safe
public class Singleton {
public static final Singleton INSTANCE = new Singleton();
private Singleton() {
if (INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
}
- It is initialized when the class is loaded, hence it is eager.
- We access the instance through a public static member, not through a public static method.
- The constructor includes a check to guarantee that there is only one instance.
- It is thread-safe since initialization is performed at class loading phase.
3.2. Eager Initialization, Static Factory Method, Thread-safe
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
if (INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Singleton getInstance() {
return INSTANCE;
}
}
- It is initialized when the class is loaded, hence it is eager.
- We access the instance through a public static method
- The constructor includes a check to guarantee that there is only one instance.
- It is thread-safe since initialization is performed at class loading phase.
3.3. Enum Singleton
public enum Singleton {
INSTANCE;
}
- The language guarantees that there will be only one instance.
- This is the recommended eager implementation.
3.4. Lazy Initialization, Synchronized, Thread-safe
public class Singleton {
private static Singleton instance;
private Singleton() {
if (instance != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- It is initialized when the instance is requested, hence it is lazy.
- We access the instance through a public static method
- The constructor includes a check to guarantee that there is only one instance.
- It is thread-safe since the accessor method is synchronized.
3.5. Lazy Initialization, Double-checked Locking, Thread-safe
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
if (instance != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- It is initialized when the instance is first requested, hence it is lazy.
- We access the instance through a public static method
- The constructor includes a check to guarantee that there is only one instance.
- It is thread-safe since we're using double-checked locking in the accessor method.
3.6. Lazy Initialization, Holder Class, Thread-safe
public class Singleton {
private static class SingletonHolder {
static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
if (SingletonHolder.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- It is initialized when the instance is requested, hence it is lazy.
- We access the instance through a public static method
- The constructor includes a check to guarantee that there is only one instance.
- It is thread-safe since the language initializes it once inside holder class.
4. Summary
In this tutorial, we've investigated the Singleton pattern and provided several implementations.
As always the source code for all examples in this tutorial is available on Github