← Back to Advanced Java Course | Chapter 6: Design Patterns | Lesson 2 of 14

Java Singleton Pattern

The singleton pattern makes sure there is only one copy of something in the whole program, like one principal in a school. Everyone shares that same one.
Syntax
java
class Singleton {
    private static Singleton instance;
    private Singleton() { }
    public static Singleton getInstance() {
        if (instance == null) instance = new Singleton();
        return instance;
    }
}

Eager Initialization

Eager initialization creates the Singleton's one and only instance the moment the class is loaded by the JVM, regardless of whether it ends up being used. It's the simplest approach to implement, but its drawback is that it always pays the cost of instantiation even in a run where the singleton is never actually needed.

Example: Eager Initialization

java
public class Main {
	static class Singleton {
		static final Singleton INSTANCE = new Singleton(); // created at class-load time, whether used or not
		private Singleton() {}
	}
	public static void main(String[] args) {
		System.out.println(Singleton.INSTANCE == Singleton.INSTANCE);
	}
}

Lazy Initialization

Lazy initialization instead delays creating the instance until the first time it's actually requested, avoiding unnecessary work up front. To make lazy initialization safe under concurrent access, the creation logic needs to be wrapped in a synchronized method or block so two threads can't both create separate instances.

Example: Lazy Initialization

java
public class Main {
	static class Singleton {
		private static Singleton instance;
		private Singleton() {}
		static synchronized Singleton getInstance() {
			if (instance == null) instance = new Singleton(); // created only when first requested
			return instance;
		}
	}
	public static void main(String[] args) {
		System.out.println(Singleton.getInstance() == Singleton.getInstance());
	}
}

Double-Checked Locking

Double-checked locking reduces the overhead of synchronizing on every single access by checking whether the instance already exists twice: once before acquiring the lock, and once again after acquiring it, only creating the instance if it's still missing both times. The instance field must be declared volatile for this pattern to be safe, since without it, other threads could observe a partially-constructed object.

Example: Double-Checked Locking

java
// Define the class Main
public class Main {
	// Define the class Singleton
	static class Singleton {
		private static volatile Singleton instance;
		private Singleton() {}
		static Singleton getInstance() {
			// Check whether instance == null
			if (instance == null) {
				// synchronized lets only one thread run this code at a time
				synchronized (Singleton.class) {
					if (instance == null) instance = new Singleton();
				}
			}
			// Send instance back to the caller
			return instance;
		}
	}
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		// Print a line to the console
		System.out.println(Singleton.getInstance() == Singleton.getInstance());
	}
}

Enum Singleton

Enum singletons get thread safety and protection against duplicate instantiation via serialization essentially for free, courtesy of guarantees baked into how the JVM handles enums. Many experienced Java developers consider this the simplest and most robust way to implement a Singleton in practice.

Example: Enum Singleton

java
public class Main {
	enum Singleton {
		INSTANCE;
		void doWork() { System.out.println("Working"); }
	}
	public static void main(String[] args) {
		Singleton.INSTANCE.doWork(); // thread-safe and serialization-safe for free
	}
}

Serialization & Reflection Protection

A conventional (non-enum) Singleton can accidentally end up duplicated during deserialization, or even via reflection forcibly calling a private constructor a second time. Implementing the readResolve() method lets you intercept deserialization and return the existing singleton instance instead of a new one.

Example: Serialization & Reflection Protection

java
import java.io.*;
public class Main {
	static class Singleton implements Serializable {
		static final Singleton INSTANCE = new Singleton();
		private Singleton() {}
		protected Object readResolve() {
			return INSTANCE; // returns the existing instance instead of a new one during deserialization
		}
	}
	public static void main(String[] args) {
		System.out.println(Singleton.INSTANCE);
	}
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Using lazy initialization with if (instance == null) instance = new Singleton(); in multi-threaded code, which can create two instances.
  2. Using double-checked locking without marking the field volatile, which can let another thread see a half-built object.
  3. Leaving the constructor public, so any code can call new Singleton() and the pattern is broken.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.