StackStalk
  • Home
  • Java
    • Java Collection
    • Spring Boot Collection
  • Python
    • Python Collection
  • C++
    • C++ Collection
    • Progamming Problems
    • Algorithms
    • Data Structures
    • Design Patterns
  • General
    • Tips and Tricks

Saturday, November 30, 2013

Singleton pattern in Java

 November 30, 2013     Java     No comments   

Approaches to implementing Singleton pattern in Java

Singleton is a creation design pattern to provide one and only instance of an object. In simple terms to achieve a singleton we need to (1) make the constructors of the class private, (2) create and store the object privately and (3) provide access to the created instance through a public method. There are several approaches to implement thread safe singleton pattern in Java. In this article we will explore some these approaches and analyze.

Lazy Initialization

In Lazy Initialization approach we use the double check locking mechanism. 
  1. First we check if the instance is already initialized. In that case we return the object immediately without any locking. 
  2. If the object is not initialized we obtain the lock. 
  3. We again check if the instance is initialized. This is necessary if another thread which obtained the lock first has already done the initialization. If initialized return the object.
  4. Otherwise, create and return the object.
It is also necessary that while storing the instance we need to associate the "volatile" keyword. The volatile keyword ensures that created instance is always written and read from RAM and not any local registers to prevent any issues when interleaving of threads.
The example below shows a sample implementation with Lazy Initialization approach.
public class LazyInitTest {

 // Store the object privately
 private static volatile LazyInitTest instance = null;
 
 // Make constructor private
 private LazyInitTest() {
  System.out.println("In LazyInitTest ...");
 }
 
 // Provide a public function to access the object
 public static LazyInitTest getInstance() {
  if ( instance == null ) {
   synchronized(LazyInitTest.class) {
    if ( instance == null ) {
     instance = new LazyInitTest();
    }
   }
  }
  return instance;
 }
 
 // Test code
 public static void main(String[] args) {
  
  // First thread
  Thread t1 = new Thread(new Runnable() {
   @Override
   public void run() {
    LazyInitTest obj = LazyInitTest.getInstance();
   }   
  });
  t1.start();
  
  // Second thread
  Thread t2 = new Thread(new Runnable() {
   @Override
   public void run() {
    LazyInitTest obj = LazyInitTest.getInstance();    }   
  });
  t2.start();
 }
}
Output:-
In LazyInitTest ...
The Lazy Initialization approach creates the object only when it is actually needed. For large objects this would be an advantage. We have the overhead of concurrency checks in this approach.

Eager Initialization

In the Eager Initialization approach we always create the object. The object is created much before it is actually needed or used.
The example below shows a sample implementation with Eager Initialization approach.
public class EagerInit {

 // Store the object privately
 private static final EagerInit instance = new EagerInit();
 
 // Make constructor private
 private EagerInit() {
  System.out.println("In EagerInitTest ...");
 }
 
 // Provide a public function to access the object
 public static EagerInit getInstance() {
  return instance;
 }
 
 // Test code
 public static void main(String[] args) {
  
  // First thread
  Thread t1 = new Thread(new Runnable() {
   @Override
   public void run() {
    EagerInit obj = EagerInit.getInstance();    
   }   
  });
  t1.start();
  
  // Second thread
  Thread t2 = new Thread(new Runnable() {
   @Override
   public void run() {
    EagerInit obj = EagerInit.getInstance();
   }   
  });
  t2.start();
 }
}
Output:-
In EagerInitTest ...
The Eager Initialization approach creates the object much before it is actually needed. For large objects this would be an disadvantage. We don't have the cost of concurrency checks in this approach.

Enum approach

The most simplest approach to implementing Singleton's is the enum approach. Leverages the guarantee from Java that an enum value is initialized only once and is thread safe. The example below shows a sample implementation with Enum approach.
public enum EnumTest {
 INSTANCE;
 
 // Test code
 public static void main(String[] args) {
  
  // First thread
  Thread t1 = new Thread(new Runnable() {
   @Override
   public void run() {
    EnumTest obj = EnumTest.INSTANCE;
   }   
  });
  t1.start();
  
  // Second thread
  Thread t2 = new Thread(new Runnable() {
   @Override
   public void run() {
    EnumTest obj = EnumTest.INSTANCE;
   }   
  });
  t2.start();
 }
}

Conclusion

The recommended approach to implementing Singleton design pattern in Java is using the Enum approach. The Enum approach is very simple and thread safe implicitly. If a class implements Serializable interface multiple objects would get created when we do readObject during de-serialization. The Enum approach has no drawbacks regarding serializable objects.
  • Share This:  
Newer Post Older Post Home

0 comments:

Post a Comment

Follow @StackStalk
Get new posts by email:
Powered by follow.it

Popular Posts

  • Avro Producer and Consumer with Python using Confluent Kafka
    In this article, we will understand Avro a popular data serialization format in streaming data applications and develop a simple Avro Produc...
  • Monitor Spring Boot App with Micrometer and Prometheus
    Modern distributed applications typically have multiple microservices working together. Ability to monitor and manage aspects like health, m...
  • Server-Sent Events with Spring WebFlux
    In this article we will review the concepts of server-sent events and work on an example using WebFlux. Before getting into this article it ...
  • Implement caching in a Spring Boot microservice using Redis
    In this article we will explore how to use Redis as a data cache for a Spring Boot microservice using PostgreSQL as the database. Idea is to...
  • Python FastAPI microservice with Okta and OPA
    Authentication (AuthN) and Authorization (AuthZ) is a common challenge when developing microservices. In this article, we will explore how t...
  • Spring Boot with Okta and OPA
    Authentication (AuthN) and Authorization (AuthZ) is a common challenge when developing microservices. In this article, we will explore how t...
  • Getting started with Kafka in Python
    This article will provide an overview of Kafka and how to get started with Kafka in Python with a simple example. What is Kafka? ...
  • Getting started in GraphQL with Spring Boot
    In this article we will explore basic concepts on GraphQL and look at how to develop a microservice in Spring Boot with GraphQL support. ...

Copyright © StackStalk