top of page

Java Singleton Design Pattern Best Practices with Examples

Introduction

One of the Gang of Four design patterns, the Java Singleton Pattern belongs to the Creational Design Pattern subcategory. Although it seems like a simple design pattern from the definition, there are many issues with its execution.

We will examine the singleton design pattern's ideas, various implementation strategies, and some recommended practices for its application in this post.


Principles of Singleton Patterns

  • The singleton pattern limits the number of times a class can be instantiated and makes sure that there is only one instance of the class running on the Java Virtual Machine.

  • To obtain a class instance, a singleton class must offer a global access point.

  • Logging, drivers objects, caching, and thread pools all leverage the singleton pattern.

  • Other design patterns, such as Abstract Factory, Builder, Prototype, and Facade, also use the singleton design pattern.

  • Core Java classes also make advantage of the singleton design paradigm (for example, java.lang.Runtime, java.awt.Desktop).


Implementation of the Java Singleton Pattern

We have a variety of methods for putting a singleton design into practice, but they all share the notions listed below.

  • To prevent other classes from creating instances of the class, it has a private function Object() { [native code] }.

  • private static variable belonging to the same class as the sole other instance.

  • The singleton class's global access point for getting its instance is a public static method that returns the class instance.

We will discover various methods for implementing the singleton pattern and design issues with the implementation in later parts.


1. Eager initialization


When using eager initialization, the singleton class instance is created as soon as the classes are loaded. Eager initialization has the problem of creating the method even though the client program may not be using it. The static initialization singleton class is implemented as follows:





This strategy should be used if your singleton class doesn't consume a lot of resources. However, singleton classes are typically made for resources like file systems, database connections, etc. Unless the client uses the getInstance method, we should refrain from instantiating anything. Additionally, there are no choices for managing exceptions with this approach.


2. Static block initialization


Similar to eager initialization, static block initialization creates an instance of the class in the static block that offers the option of handling exceptions.





It is not recommended to utilize eager initialization or static block initialization because they both build the instance before it is used.


3. Lazy Initialization


The singleton pattern's lazy initialization approach builds the instance in the global access method. The sample code for using this method to create a singleton class is shown below:




The aforementioned implementation is effective in a single-threaded environment, but it might lead to problems in multi-threaded environments if many threads are running simultaneously inside the if condition. The singleton pattern will be destroyed, and both threads will receive unique instances of the singleton class as a result. We'll examine various approaches to developing a thread-safe singleton class in the following section.


4. Thread Safe Singleton


Making the global access method synchronised so that only one thread can use it at a time is an easy technique to develop a singleton class that is thread-safe. Here is how this strategy is generally applied:





The previous implementation is effective and offers thread safety, but efficiency is sacrificed due to the expense of the synchronised procedure, even though it is only necessary for the first few threads that might generate separate instances. The double-checked locking principle is utilised to prevent this additional overhead each time. This method uses an additional check to ensure that only one instance of a singleton class is produced by inserting the synchronised block within the if condition. The implementation of double-checked locking is seen in the code sample below:





5. Bill Pugh Singleton Implementation


The Java memory model had several problems before Java 5, and earlier solutions would frequently fail when an excessive number of threads attempted to access an instance of a singleton class at once. In order to construct the singleton class, Bill Pugh came up with a new method employing an inner static helper class. Here is an illustration of how Bill Pugh Singleton is put into practise:





Take note of the private inner static class that houses the singleton class instance. Only when the getInstance() function is called does the SingletonHelper class load into memory and generate an instance of the singleton class, not when the singleton class is loaded itself. Due to the lack of synchronisation, this is the singleton class technique that is most frequently employed.


6. Using Reflection to destroy Singleton Pattern


All prior singleton implementation techniques can be eliminated via reflection. Here is a class illustration:





When you run the preceding test class, you will notice that the hashCode of both instances is different, destroying the singleton pattern. Reflection is a powerful tool that is used in many frameworks, including Spring and Hibernate. Continue your education with the Java Reflection Tutorial.


7. Enum Singleton


To address this issue with Reflection, Joshua Bloch recommends using enum to implement the singleton design pattern, as Java ensures that any enum value is instantiated only once in a Java programme. Because Java Enum values are globally accessible, the singleton is as well. The disadvantage is that the enum type is somewhat rigid (for example, it does not allow lazy initialization).





8. Serialization and Singleton


In distributed systems, we may need to implement the Serializable interface in the singleton class so that we can save its state to the file system and retrieve it later. Here's a small singleton class that also implements the Serializable interface:





The issue with serialised singleton classes is that whenever we deserialize them, a new instance of the class is created. Here's an illustration:





As a result, the singleton pattern is destroyed. To avoid this scenario, we simply need to provide an implementation of the readResolve() method.





Following this, you will notice that the hashCode of both instances in the test programme is the same. Learn more about Java Serialization and Deserialization.


Conclusion


The singleton design pattern was discussed in this article.

Continue your education with additional Java tutorials.

421 views0 comments

Recent Posts

See All

Comentários


bottom of page