Understanding the Singleton Pattern in Java: Concepts, Comparisons, and Implementations
This article explains the Singleton design pattern in Java, outlines its characteristics, compares it with static classes, clarifies common misconceptions, and presents multiple thread‑safe and non‑thread‑safe implementation techniques with code examples.
1. What Is the Singleton Pattern
When a process requires that only one instance of a class exists at any time, the Singleton pattern is used to ensure a single, globally accessible object.
2. Characteristics of the Singleton Pattern
Only one instance can exist.
The singleton class creates its own unique instance.
The singleton instance is provided to other objects.
3. Singleton vs. Static Class
Although both provide a single access point, singletons can be inherited and overridden, support lazy loading, and are better suited for stateful resources such as DAO layers, whereas static classes are initialized once and have higher access efficiency.
Common Misconceptions About Static Classes
Misconception 1: Static methods always stay in memory while instance methods do not – actually, specially written instance methods can also be resident.
Misconception 2: Static methods reside on the heap and instance methods on the stack – both are loaded into a read‑only code memory area.
Choosing Between Static Class and Singleton
Use a static class when no state needs to be maintained; use a singleton when specific state must be preserved across the application.
4. Implementations of the Singleton Pattern
4.1 Lazy Initialization (Non‑Thread‑Safe)
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo() {}
public static SingletonDemo getInstance() {
if (instance == null) {
instance = new SingletonDemo();
}
return instance;
}
}This version creates the instance on first use but is not safe for concurrent access.
4.2 Thread‑Safe Lazy Initialization (Synchronized)
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo() {}
public static synchronized SingletonDemo getInstance() {
if (instance == null) {
instance = new SingletonDemo();
}
return instance;
}
}Adding synchronization makes it safe but incurs a performance penalty.
4.3 Eager Initialization (Thread‑Safe)
public class SingletonDemo {
private static SingletonDemo instance = new SingletonDemo();
private SingletonDemo() {}
public static SingletonDemo getInstance() {
return instance;
}
}The instance is created when the class loads, eliminating lazy loading.
4.4 Static Inner‑Class Holder (Thread‑Safe, Lazy)
public class SingletonDemo {
private static class SingletonHolder {
private static final SingletonDemo INSTANCE = new SingletonDemo();
}
private SingletonDemo() {
System.out.println("Singleton has loaded");
}
public static SingletonDemo getInstance() {
return SingletonHolder.INSTANCE;
}
}The inner static class is not loaded until getInstance() is called, providing lazy loading with thread safety.
4.5 Enum Singleton (Thread‑Safe)
enum SingletonDemo {
INSTANCE;
public void otherMethods() {
System.out.println("Something");
}
}Recommended by Joshua Bloch, this approach guarantees a single instance, serialization safety, and protection against reflection.
4.6 Double‑Checked Locking (Usually Thread‑Safe)
public class SingletonDemo {
private static volatile SingletonDemo instance;
private SingletonDemo() {
System.out.println("Singleton has loaded");
}
public static SingletonDemo getInstance() {
if (instance == null) {
synchronized (SingletonDemo.class) {
if (instance == null) {
instance = new SingletonDemo();
}
}
}
return instance;
}
}This pattern reduces synchronization overhead while preserving lazy loading.
4.7 Volatile‑Based Singleton
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}Marking the instance as volatile prevents instruction reordering that could expose a partially constructed object.
4.8 ThreadLocal Singleton (Thread‑Safe)
public class Singleton {
private static final ThreadLocal<Singleton> tlSingleton = new ThreadLocal<Singleton>() {
@Override
protected Singleton initialValue() {
return new Singleton();
}
};
public static Singleton getInstance() {
return tlSingleton.get();
}
private Singleton() {}
}Each thread gets its own singleton instance, eliminating contention.
4.9 CAS‑Based Singleton (Thread‑Safe)
public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();
private Singleton() {}
public static final Singleton getInstance() {
for (;;) {
Singleton current = INSTANCE.get();
if (current != null) {
return current;
}
current = new Singleton();
if (INSTANCE.compareAndSet(null, current)) {
return current;
}
}
}
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}Uses AtomicReference and compare‑and‑set to achieve lock‑free thread safety.
Each of these implementations balances trade‑offs among lazy loading, performance, and thread safety, allowing developers to select the most appropriate version for their specific scenario.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
