DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

How does AI transform chaos engineering from an experiment into a critical capability? Learn how to effectively operationalize the chaos.

Data quality isn't just a technical issue: It impacts an organization's compliance, operational efficiency, and customer satisfaction.

Are you a front-end or full-stack developer frustrated by front-end distractions? Learn to move forward with tooling and clear boundaries.

Developer Experience: Demand to support engineering teams has risen, and there is a shift from traditional DevOps to workflow improvements.

Related

  • Singleton: 6 Ways To Write and Use in Java Programming
  • Java String: A Complete Guide With Examples
  • Generics in Java and Their Implementation
  • How to Estimate Object Memory Allocation in Java

Trending

  • MLOps: Practical Lessons from Bridging the Gap Between ML Development and Production
  • Exceptions in Lambdas
  • The Rise of Vibe Coding: Innovation at the Cost of Security
  • Understanding the Shift: Why Companies Are Migrating From MongoDB to Aerospike Database?
  1. DZone
  2. Coding
  3. Languages
  4. Singletons: Bill Pugh Solution or Enum

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.

By 
Harinath Kuntamukkala user avatar
Harinath Kuntamukkala
·
Updated Oct. 09, 17 · Tutorial
Likes (11)
Comment
Save
Tweet
Share
45.1K Views

Join the DZone community and get the full member experience.

Join For Free

A 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.

Thread safety Blocks Serialization Java (programming language) Lazy loading Lazy initialization Implementation Object (computer science) Memory (storage engine)

Published at DZone with permission of Harinath Kuntamukkala. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Singleton: 6 Ways To Write and Use in Java Programming
  • Java String: A Complete Guide With Examples
  • Generics in Java and Their Implementation
  • How to Estimate Object Memory Allocation in Java

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: