An Efficient Way to Implement a Singleton Pattern in Java

The singleton design pattern is a creational pattern that ensures the existence of only one instance of a class in a Java program. This pattern is commonly used in scenarios where only one instance of a class is required to control actions throughout the program.

Introduction to Singleton Pattern

The singleton pattern involves creating a class with a method that returns the same instance of the class every time it is called. This means that no matter how many times you request an object from this class, you will always get the same instance.

Implementing the Singleton Pattern in Java

There are several ways to implement a singleton pattern in Java. Here, we will discuss two widely used approaches: the eager initialization approach and the lazy initialization approach.

1. Eager Initialization

In the eager initialization approach, the instance of the class is created at the time of class loading, ensuring that the instance is always available for use.

            
                public class EagerInitializedSingleton {
                    private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();

                    private EagerInitializedSingleton() {}

                    public static EagerInitializedSingleton getInstance() {
                        return instance;
                    }
                }
            
        

In this implementation, the EagerInitializedSingleton class has a private static final instance of itself, which is created when the class is loaded. The constructor is private to prevent instantiation of the class from outside.

To access the instance, you can call the getInstance() method, which always returns the same instance.

2. Lazy Initialization

In the lazy initialization approach, the instance of the class is created only when it is requested for the first time. This approach saves resources as the instance is created only when it is actually needed.

            
                public class LazyInitializedSingleton {
                    private static LazyInitializedSingleton instance;

                    private LazyInitializedSingleton() {}

                    public static LazyInitializedSingleton getInstance() {
                        if (instance == null) {
                            instance = new LazyInitializedSingleton();
                        }
                        return instance;
                    }
                }
            
        

In this implementation, the LazyInitializedSingleton class also has a private constructor and a static method getInstance() to access the instance. However, the instance is created only when the getInstance() method is called for the first time. Subsequent calls will return the previously created instance.

Thread Safety in Singleton Pattern

One important aspect to consider when implementing a singleton pattern is thread safety. Thread safety ensures that multiple threads can access the singleton instance without causing any issues.

In the above implementations, both eager initialization and lazy initialization approaches are not thread-safe. This means that if multiple threads try to access the instance simultaneously, it may result in multiple instances being created.

1. Thread-Safe Eager Initialization

To make the eager initialization approach thread-safe, we can use the double-checked locking technique.

            
                public class ThreadSafeEagerInitializedSingleton {
                    private static volatile ThreadSafeEagerInitializedSingleton instance;

                    private ThreadSafeEagerInitializedSingleton() {}

                    public static ThreadSafeEagerInitializedSingleton getInstance() {
                        if (instance == null) {
                            synchronized (ThreadSafeEagerInitializedSingleton.class) {
                                if (instance == null) {
                                    instance = new ThreadSafeEagerInitializedSingleton();
                                }
                            }
                        }
                        return instance;
                    }
                }
            
        

In this implementation, the volatile keyword ensures that multiple threads handle the instance correctly when it is being initialized. The double-checked locking technique is used to reduce the overhead of locking the entire method during every call.

2. Thread-Safe Lazy Initialization

To make the lazy initialization approach thread-safe, we can use the synchronized keyword to synchronize the getInstance() method.

            
                public class ThreadSafeLazyInitializedSingleton {
                    private static ThreadSafeLazyInitializedSingleton instance;

                    private ThreadSafeLazyInitializedSingleton() {}

                    public static synchronized ThreadSafeLazyInitializedSingleton getInstance() {
                        if (instance == null) {
                            instance = new ThreadSafeLazyInitializedSingleton();
                        }
                        return instance;
                    }
                }
            
        

In this implementation, the synchronized keyword ensures that only one thread can access the getInstance() method at a time, preventing multiple instances from being created simultaneously.

Conclusion

The singleton pattern is a widely used design pattern in Java that ensures the existence of only one instance of a class throughout the program. In this article, we discussed two approaches to implement the singleton pattern: eager initialization and lazy initialization. We also explored the thread safety aspect of the singleton pattern and provided thread-safe versions of the implementations.