In this post, we will learn about Singleton design pattern principles, different
ways to implement Singleton and some of the best practices for it’s
usage.
Singleton pattern restricts the instantiation of a class and ensures that
only one instance of the class exists in the java virtual machine. The
singleton class must provide a global access point to get the instance
of the class.
Singleton pattern is used for logging, drivers objects, caching and thread pool.
Singleton design pattern is also used in other design patterns like Abstract Factory, Builder, Prototype, Facade etc.
To implement the Singleton Design Pattern,you do the following the things,
1) private constructor - no other class can instantiate a new object.
2) private reference - no external modification.
3) public static method is the only place that can get an object.
Below are the different approaches of Singleton pattern implementation and design.
Eager initialization
In eager initialization, the instance of Singleton Class is created
at the time of class loading, this is the easiest method to create a
singleton class but it has a drawback that instance is created even
though client application might not be using it.
Here is the implementation of static initialization singleton class.
public class EagerInitializer{
private static final EagerInitializer instance = new EagerInitializer();
//private constructor to avoid client applications to use constructor.
private EagerInitializer(){
}
public static EagerInitializer getInstance(){
return instance;
}
}
If your singleton class is not using a lot of resources, this is the
approach to use. But in most of the scenarios, Singleton classes are
created for resources such as File System, Database connections etc and
we should avoid the instantiation until unless client calls the
getInstance
method. Also this method doesn’t provide any options for exception handling.
Static block initialization
Static block
initialization implementation is similar to eager initialization,
except that instance of class is created in the static block that
provides option for exception handling
public class StaticInitializer {
private static StaticInitializer instance ;
//private constructor to avoid client applications to use constructor.
private StaticInitializer() {
}
static {
try {
instance = new StaticInitializer();
} catch(Exception e) {
throw new RuntimeException("Exception in static block");
}
}
public static StaticInitializer getInstance() {
return instance;
}
}
Both eager initialization and static block initialization creates the
instance even before it’s being used and that is not the best practice
to use. So in further sections, we will learn how to create Singleton
class that supports lazy initialization.
Lazy Initialization
Lazy initialization method to implement Singleton pattern creates the
instance in the global access method. Here is the sample code for
creating Singleton class with this approach.
public class LazyInitializer{
private static LazyInitializer instance;
//private constructor to avoid client applications to use constructor.
private LazyInitializer(){
}
public static LazyInitializer getInstance(){
if(instance == null){
instance = new LazyInitializer();
}
return instance;
}
}
The above implementation works fine incase of single threaded
environment but when it comes to multithreaded systems, it can cause
issues if multiple threads are inside the if loop at the same time. It
will destroy the singleton pattern and both threads will get the
different instances of singleton class. In next section, we will see
different ways to create a thread-safe singleton class.
Thread Safe Singleton
The easier way to create a thread-safe singleton class is to make the global access method synchronized, so that only one thread can execute this method at a time. General implementation of this approach is like the below class.
public class ThreadSafe{
private static ThreadSafe instance ;
//private constructor to avoid client applications to use constructor.
private ThreadSafe(){
}
public static synchronized ThreadSafe getInstance(){
if(instance == null){
instance = new ThreadSafe();
}
return instance;
}
}
Above implementation works fine and provides thread-safety but it
reduces the performance because of cost associated with the synchronized
method, although we need it only for the first few threads who might
create the separate instances (Read: Java Synchronization). To avoid this extra overhead every time,
double checked locking
principle is used. In this approach, the synchronized block is used
inside the if condition with an additional check to ensure that only one
instance of singleton class is created.
Below code snippet provides the double checked locking implementation
public static ThreadSafe getInstance(){
if(instance == null){
synchonized(ThreadSafe.class){
if(instance == null ){
instance = new ThreadSafe();
}
}
}
return instance;
}
Real time Examples for Singleton Design Pattern:---
JDBC Example using Singleton Design pattern:--
We write a class (ConnectionFactory) which implements singleton pattern defining database connection configuration statements and methods to make connection to the database. Reason for making this class as singleton is, we can create one object of this class and can create many Connection objects (one factory, many objects).
package com.adnjavainterview;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionFactory {
//static reference to itself
private static ConnectionFactory instance = new ConnectionFactory();
public static final String URL = "jdbc:mysql://localhost/jdbcdb";
public static final String USER = "YOUR_DATABASE_USERNAME";
public static final String PASSWORD = " YOUR_DATABASE_PASSWORD";
public static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
//private constructor
private ConnectionFactory() {
try {
Class.forName(DRIVER_CLASS);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private Connection createConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection(URL, USER, PASSWORD);
}
catch (SQLException e) {
System.out.println("ERROR: Unable to Connect to Database.");
}
return connection;
}
public static Connection getConnection() {
return instance.createConnection();
}
}
Thank you for visiting blog..
Related Post:--