Singletons with needles and thread

Two approaches to creating thread-safe singletons

January 25, 2002

In "Singletons Rule," "Effective Object-Oriented Design," and "n Class Instances," I addressed questions regarding the Singleton design pattern. As many readers have since pointed out, you must take special care to ensure that singletons are thread safe. With that in mind, in this Java Q&A I explore singletons and thread safety.

Let's begin by looking at a nonthread-safe singleton:

public class Singleton {
    private static Singleton instance;
    
    public static Singleton getInstance() {
        if( instance == null ) {
            instance = new Singleton();
        }
        return instance;
    }
    
    private Singleton() {}
    public static void main( String [] args ) {
        Singleton instance = Singleton.getInstance();
        // ... manipulate instance
    }
   
}

Unfortunately, in the example above, a thread may at any time pre-empt the call to getInstance(). For example, a thread may pre-empt a running thread at instance = new Singleton. If that were to happen, multiple Singleton instances might instantiate, thus defeating the purpose of a singleton.

To make a singleton thread safe, you have two choices. The first simply synchronizes the getInstance() method:

public class Singleton {
    private static Singleton instance;
    
    public synchronized static Singleton getInstance() {
        if( instance == null ) {
            instance = new Singleton();
        }
        return instance;
    }
    
    private Singleton() {}
    public static void main( String [] args ) {
        Singleton instance = Singleton.getInstance();
        // ...
    }
}

Synchronizing the method guarantees that a call to the method cannot be interrupted.

The second approach to thread safety declares a constant Singleton attribute on the Singleton class itself:

public class Singleton {
    public final static Singleton instance = new Singleton();
    
    private Singleton() {}
    public static void main( String [] args ) {
        Singleton instance = Singleton.instance;
        // ...
    }
    
}

instance will initialize when the class loades. Both solutions guarantee only one Singleton will exist.

In "n Class Instances," I presented an alternate approach to singletons that allows multiple instances of an object but caps the number of instances at some number. In that article, the following method retrieved the instance:

public static AlternateSingleton instance()
{
    return instances.next();
}

As you can see, instance() is not thread safe because it retrieves the instance from a nonthread-safe data structure. If a thread pre-empts instances.next() at the wrong time, the same instance may be returned multiple times in a row. Fortunately, fixing the problem is as easy as synchronizing the method:

public synchronized static AlternateSingleton instance()
{
    return instances.next();
}

Singleton discussion

Not everyone believes singletons represent the best design choice, while others believe that singletons have their place when used carefully.

When thinking about using a singleton, it is a good idea to check out the arguments for and against the pattern.

As one drawback, the Singleton as I implemented above makes inheritance impossible (the constructor is private). So you can lose some object-oriented programming (OOP) benefits when you decide to make a class a singleton. However, other approaches to implementing the Singleton pattern allow you to use inheritance or to treat the singleton polymorphically. For example, you can combine an interface, multiple implementations, and a factory to boost your singletons' flexibility. Let's examine each piece of this alternate approach.

The interface

The interface defines the Singleton type. If you define the singleton in terms of an interface, you can have many different implementations.

Let's consider a singleton that writes logs:

public interface LogSingleton {
    public void logError( String error );
    public void logWarning( String warning );
    public void logDebug( String debug );
}

The interface above defines a number of methods for writing out a log. The implementation determines whether to write the logs to the screen, a file, or a socket.

Multiple implementations

Once you have defined an interface, you can have as many implementations as you wish. The following implementation simply logs to the screen:

 
class LogImpl implements LogSingleton {
    public LogImpl() {}
    public void logWarning(String warning) {
        System.out.println( "WARNING: " + warning );
    }
    
    public void logError(String error) {
        System.out.println( "ERROR: " + error );
    }
    
    public void logDebug(String debug) {
        System.out.println( "DEBUG: " + debug );
    }
    
}

Note that this singleton implementation differs from the one I presented earlier. Here, the constructor is public and there is no getInstance() method. In this approach the singleton implementation does not enforce the one instance limitation. Instead, we'll leave those details to the singleton factory.

In this approach, you gain a little more freedom. For example, you can use one implementation as a base class in an inheritance hierarchy because the constructor is not private. Multiple LogSingleton implementations let you treat that class polymorphically. With that in mind, you might create an implementation that writes to a file and another that writes to a stream.

Remember, when an object is said to be a singleton, you allow only one instance of that object to exist at any given time. So you'll want to limit instantiation of the implementations to the factory only. You can do so by creating the implementations in the factory's package and declaring the implementations as package protected. That way, only the factory can see and instantiate the implementations. (Which explains why I left public out of the class declaration.)

The singleton factory

The singleton factory creates the singleton instance and guarantees that there will be only one instance in the system at any given time.

Here's an implementation of a factory that will return the LogImpl:

public abstract class AbstractLogFactory {
    private static AbstractLogFactory factory = new SystemLogFactory();
    
    public static AbstractLogFactory getFactory() {
        return factory;
    }
    
    public abstract LogSingleton log();
    
}
class SystemLogFactory extends AbstractLogFactory {
    private static LogSingleton log = new LogImpl();
    public LogSingleton log() {
        return log;
    }
}

If you program your objects to retrieve a factory only from the AbstractLogFactory, you can switch logs without changing the classes that use the factory. When a new log you wish to use comes along, simply create a subclass of AbstractLogFactory that instantiates the proper type of log and update AbstractLogFactory to return the proper factory type.

Thus, if you would rather return a FileLog, you could make the following changes:

public abstract class AbstractLogFactory {
    private static AbstractLogFactory factory = new FileLogFactory();
    
    public static AbstractLogFactory getFactory() {
        return factory;
    }
    
    public abstract LogSingleton log();
    
}
class FileLogFactory extends AbstractLogFactory {
    private static LogSingleton log = new FactoryLog ();
    
    public LogSingleton log() {
        return log;
    }
    
}

This alternate approach to singleton creation proves more flexible than other approaches because you move the Singleton-pattern enforcement responsibility from the implementation and to another object. In fact, this approach's flexibility lets you change from returning a singleton to creating and returning multiple instances -- all without the rest of your program knowing.

Tony Sintes is an independent consultant and founder of First Class Consulting, Inc., a consulting firm that specializes in the bridging of disparate enterprise systems and training. Outside of First Class Consulting, Tony is an active freelance writer as well as author of Sams Teach Yourself Object-Oriented Programming in 21 Days (Sams, 2001; ISBN: 0672321092).

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more