Skip to content
DeveloperMemos

Creating Singletons in Java

Java, Singleton Design Pattern2 min read

Singletons are a design pattern used in software development to ensure that a class has only one instance and provides a global point of access to it. In Java, creating singletons is straightforward and can be accomplished using various techniques. In this article, we will explore some of the common approaches to creating singletons in Java, along with examples.

Eager Initialization

The eager initialization approach involves creating an instance of the singleton class at the time of class loading. Here's an example of a singleton class created using eager initialization:

1public class EagerSingleton {
2 private static final EagerSingleton INSTANCE = new EagerSingleton();
3
4 private EagerSingleton() {
5 // Private constructor to prevent instantiation
6 }
7
8 public static EagerSingleton getInstance() {
9 return INSTANCE;
10 }
11
12 // Other methods and fields
13}

In the above example, the INSTANCE variable is declared as final and static, ensuring that it is a class-level variable accessible without creating an instance of the class. The constructor is marked as private, preventing external classes from instantiating the singleton. The getInstance() method provides a global point of access to the singleton instance.

Lazy Initialization

Lazy initialization defers the creation of the singleton instance until it is actually needed. This approach is useful when the singleton is resource-intensive or requires complex initialization. Here's an example of a singleton class created using lazy initialization:

1public class LazySingleton {
2 private static LazySingleton INSTANCE;
3
4 private LazySingleton() {
5 // Private constructor to prevent instantiation
6 }
7
8 public static synchronized LazySingleton getInstance() {
9 if (INSTANCE == null) {
10 INSTANCE = new LazySingleton();
11 }
12 return INSTANCE;
13 }
14
15 // Other methods and fields
16}

In the above example, the getInstance() method checks if the singleton instance has been created. If not, it creates a new instance. The method is marked as synchronized to ensure thread safety in a multi-threaded environment. While this approach provides lazy initialization, it introduces a slight performance overhead due to the synchronized keyword.

Double-Checked Locking

Double-checked locking is an optimization technique to reduce the overhead of acquiring locks. It performs a quick check of the singleton instance without acquiring the lock if the instance has already been created. Here's an example of a singleton class created using double-checked locking:

1public class DoubleCheckedSingleton {
2 private static volatile DoubleCheckedSingleton INSTANCE;
3
4 private DoubleCheckedSingleton() {
5 // Private constructor to prevent instantiation
6 }
7
8 public static DoubleCheckedSingleton getInstance() {
9 if (INSTANCE == null) {
10 synchronized (DoubleCheckedSingleton.class) {
11 if (INSTANCE == null) {
12 INSTANCE = new DoubleCheckedSingleton();
13 }
14 }
15 }
16 return INSTANCE;
17 }
18
19 // Other methods and fields
20}

In the above example, the volatile keyword ensures that changes made to the INSTANCE variable are immediately visible to other threads. The double-checked locking is performed within a synchronized block to guarantee thread safety. This approach provides both lazy initialization and improved performance in multi-threaded scenarios.

Enum Singleton

Java enums provide a concise and thread-safe way to create singletons. Enum instances are globally accessible and have implicit serialization support. Here's an example of a singleton created using an enum:

1public enum EnumSingleton {
2 INSTANCE;
3
4 // Other methods and fields
5}

In the above example, the singleton instance is created implicitly when the enum value INSTANCE is accessed. Enum singletons are thread-safe by default and can be easily serialized without losing the singleton property.

Wrap Up

Singletons play a crucial role in software design by ensuring that only one instance of a class exists throughout the application. In this article, we explored different approaches to creating singletons in Java, including eager initialization, lazy initialization, double-checked locking, and enum singletons. Each approach has its pros and cons, and the choice depends on specific requirements and use cases.