Singletons: Bill Pugh Solution or Enum
There is a variety of approaches you can take with Singleton patterns. Here, we pit Bill Pugh's solution against the handy Enum singleton.
Join the DZone community and get the full member experience.
Join For FreeA singleton class ensures that only one instance of that class is created. To ensure the point of access, the class controls the instantiation of its object. Singleton classes are found in many places in the JDK, such as java.lang.Runtime.
The Singleton class offers two things: one and only one instance of the class, and a global single point of access to that object
Implementation
To implement a Singleton pattern, we have different approaches — but all of them have the following common concepts.
- Private constructor to prevent instantiation of the class from other classes.
- Private static variable of the same class that is the only instance of the class.
- Public static method that returns the instance of the class. This is the global access point for the outside world to get the instance of the singleton class.
//Logger class must be instantiated one and only once in the application; it is to ensure that the
//application makes use of that same logger instance
public class Logger {
//declare the constructor private to prevent clients
//from instantiating an object of this class directly
private Logger() {}
//by default, this field is initialized to null
//the static method to be used by clients to get the instance of the Logger class
private static Logger myInstance;
public static Logger getInstance() {
if (myInstance == null) {
//this is the first time this method is called,
//and that's why myInstance is null
myInstance = new Logger();
}
//return the same object reference any time and
//every time getInstance is called
return myInstance;
}
//Other methods
}
Can we ensure that our Singleton is indeed a Singleton? We will discuss that later in the article.
Different Approaches
- Eager initialization: instance of a class is created long before it is actually required
- Lazy initialization: instance of a class is created when the client sends a request
- Static block initialization: is similar to eager initialization, except that the instance of the class is created in the static block that provides the option for exception handling
- Thread safe singleton: is to make the global access method is synchronized so that only one thread can execute this method at a time
- Bill Pugh singleton: implementation using the static inner helper class. Static inner classes are not loaded into memory until their getInstance() methods are called
- Enum singleton: provides implicit support for thread safety and only one instance is guaranteed
Eager initialization and lazy initialization work fine in a single-threaded environment. However, they won't be suitable for multi-threaded environments.
Static block initialization has one drawback: If we have a class with three static fields and the application needs to access only one, for which instance creation is not required at all, we still have one instance created — whether we require it or not.
A thread safe singleton works fine in multi-threaded environments but reduces performance because of the cost associated with the synchronized method.
To overcome the issue of synchronization, Bill Pugh came up with his implementation using the static inner helper class. The static inner class is not loaded into memory until its getInstance() method is called.
Meanwhile, Enum singletons provide implicit support for thread safety and only one instance is guaranteed, as per the Javadocs.
So, for this article, we're going to focus on the Bill Pugh approach and Enum singletons. Out of all other approaches, these two work well in single- and multi-threaded environments without a performance impact.
Bill Pugh Singleton
A Bill Pugh Singleton is based on the “initialization on demand holder” idiom. This idiom uses inner classes and does not use any synchronization construct. It uses static blocks, but in a different way — and suggests using static inner classes.
We will rewrite the above Logger program using the Bill Pugh approach here.
public class Logger {
private Logger() {
// private constructor
}
// static inner class - inner classes are not loaded until they are
// referenced.
private static class LoggerHolder {
private static Logger logger = new Logger();
}
// global access point
public static Logger getInstance() {
return LoggerHolder.logger;
}
//Other methods
}
The Bill Pugh Singleton is the best approach so far, but it can be easily destroyed with the use of Java reflection.
public class LoggerReflection {
public static void main(String[] args) {
Logger instance1 = Logger.getInstance();
Logger instance2 = null;
try {
Constructor[] cstr = Logger.class.getDeclaredConstructors();
for (Constructor constructor: cstr) {
//Setting constructor accessible
constructor.setAccessible(true);
instance2
= (Logger) constructor.newInstance();
break;
}
} catch (Exception e) {
System.out.println(e);
}
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
When you run the above class, you will notice that the hashCode of both the instances is not same, which destroys the singleton pattern.
Enum Singleton
To overcome this situation with Reflection, Joshua Bloch suggests the use of Enum to implement the Singleton design pattern, as Java ensures that any Enum value is instantiated only once in a Java program. However, the obvious drawback is that we cannot have lazy loading in Enum.
So, the Logger class will look like this using an Enum Singleton.
public enum Logger {
INSTANCE;
//other methods
}
To get the instance client, we need to call Logger.INSTANCE.
What if we need serialization for this Singleton?
If the Singleton class implements java.io.Serializable and a singleton object is serialized, then deserialized more than once, there will be multiple instances of the Singleton.
In order to avoid the serialization issue, implement a readResolve() method in the Logger Singleton class as below:
public class Logger implements Serializable {
//other code related to Singleton
//call this method immediately after De-serialization
//it returns an instance of singleton
protected Object readResolve() {
return getInstance();
}
}
Conclusion
We have discussed different approaches of creating a Singleton class and mainly focused on the Bill Pugh and Enum approaches. Out of these two, I feel the Enum pattern is better, as this approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiations, even in the face of sophisticated serialization or reflection attacks.
Thank you.
Published at DZone with permission of Harinath Kuntamukkala. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments