How to navigate the deceptively simple Singleton pattern

The Singleton pattern is deceptively simple, even and especially for Java developers. In this classic JavaWorld article, David Geary demonstrates how Java developers implement singletons, with code examples for multithreading, classloaders, and serialization. He concludes with a look at implementing singleton registries in order to specify singletons at runtime.

1 2 3 Page 2
Page 2 of 3

Example 6. Example 5's output

Buildfile: build.xml
init:
     [echo] Build 20030414 (14-04-2003 03:06)
compile:
run-test-text:
INFO Thread-1: sleeping...
INFO Thread-2: created singleton: Singleton@7e5cbd
INFO Thread-1: created singleton: Singleton@704ebb
junit.framework.AssertionFailedError: expected:<true> but was:<false>
   at junit.framework.Assert.fail(Assert.java:47)
   at junit.framework.Assert.failNotEquals(Assert.java:282)
   at junit.framework.Assert.assertEquals(Assert.java:64)
   at junit.framework.Assert.assertEquals(Assert.java:149)
   at junit.framework.Assert.assertEquals(Assert.java:155)
   at SingletonTest$SingletonTestRunnable.run(Unknown Source)
   at java.lang.Thread.run(Thread.java:554)
     [java] .
     [java] Time: 0.577
     [java] OK (1 test)

Now that we know Example 4's singleton is not thread-safe, let's see how we can fix it.

Synchronization

Making Example 4's singleton class thread-safe is easy—just synchronize the getInstance() method like this:

public synchronized static Singleton getInstance() {
   if(singleton == null) {
      simulateRandomActivity();
      singleton = new Singleton();
   }
   logger.info("created singleton: " + singleton);
   return singleton;
}

After we synchronize the getInstance() method, we can rerun Example 5's test case with the following results:

Buildfile: build.xml
init:
     [echo] Build 20030414 (14-04-2003 03:15)
compile:
    [javac] Compiling 2 source files
run-test-text:
INFO Thread-1: sleeping...
INFO Thread-1: created singleton: Singleton@ef577d
INFO Thread-2: created singleton: Singleton@ef577d
     [java] .
     [java] Time: 0.513
     [java] OK (1 test)

This time, the test case works and our multithreading worries are over; however, the astute reader may realize that the getInstance() method only needs to be synchronized the first time it is called. Because synchronization is very expensive performance-wise (synchronized methods can run up to 100 times slower than unsynchronized methods), perhaps we can introduce a performance enhancement that only synchronizes the singleton assignment in getInstance().

A performance enhancement

In search of a performance enhancement, you might choose to rewrite the getInstance() method like this:

public static Singleton getInstance() {
   if(singleton == null) {
      synchronized(Singleton.class) { 
         singleton = new Singleton();
      }
   }
   return singleton;
}

Instead of synchronizing the entire method, the preceding code fragment only synchronizes the critical code. However, the preceding code fragment is not thread-safe. Consider the following scenario: Thread 1 enters the synchronized block, and, before it can assign the singleton member variable, the thread is preempted. Subsequently, another thread can enter the if block. The second thread will wait for the first thread to finish, but we will still wind up with two distinct singleton instances. Is there a way to fix this problem? Read on.

Double-checked locking

Double-checked locking is a technique that, at first glance, appears to make lazy instantiation thread-safe. That technique is illustrated in the following code fragment:

public static Singleton getInstance() {
  if(singleton == null) {
     synchronized(Singleton.class) {
       if(singleton == null) {
         singleton = new Singleton();
       }
    }
  }
  return singleton;
}

What happens if two threads simultaneously access getInstance()? Imagine Thread 1 enters the synchronized block and is preempted. Subsequently, a second thread enters the if block. When Thread 1 exits the synchronized block, Thread 2 makes a second check to see if the singleton instance is still null. Since Thread 1 set the singleton member variable, Thread 2's second check will fail, and a second singleton will not be created. Or so it seems.

Unfortunately, double-checked locking is not guaranteed to work because the compiler is free to assign a value to the singleton member variable before the singleton's constructor is called. If that happens, Thread 1 can be preempted after the singleton reference has been assigned, but before the singleton is initialized, so Thread 2 can return a reference to an uninitialized singleton instance.

Since double-checked locking is not guaranteed to work, you must synchronize the entire getInstance() method. However, another alternative is simple, fast, and thread-safe.

An alternative thread-safe singleton implementation

Example 7 lists a simple, fast, and thread-safe singleton implementation:

Example 7. A simple singleton

public class Singleton {
   public final static Singleton INSTANCE = new Singleton();
   private Singleton() {
         // Exists only to defeat instantiation.
      }
}

The preceding singleton implementation is thread-safe because static member variables created when declared are guaranteed to be created the first time they are accessed. You get a thread-safe implementation that automatically employs lazy instantiation; here's how you use it:

Singleton singleton = Singleton.INSTANCE;
      singleton.dothis();
      singleton.dothat();
      ...

Of course, like nearly everything else, the preceding singleton is a compromise; if you use that implementation, you can't change your mind and allow multiple singleton instances later on. With a more conservative singleton implementation, instances are obtained through a getInstance() method, and you can change those methods to return a unique instance or one of hundreds. You can't do the same with a public static member variable.

You can safely use Example 7's singleton implementation or Example 1's implementation with a synchronized getInstance() method. However, we must explore another issue: You must specify the singleton class at compile time, which is not very flexible. A registry of singletons will let us specify singleton classes at runtime.

Use a registry

Use a singleton registry to:

  • Specify singleton classes at runtime
  • Prevent singleton subclasses from allowing multiple instances

Example 8 lists a singleton class that maintains a registry of singletons, registered by class name:

Example 8. A singleton with a registry

import java.util.HashMap;
import org.apache.log4j.Logger;
public class Singleton {
   private static HashMap map = new HashMap();
   private static Logger logger = Logger.getRootLogger();
   protected Singleton() {
      // Exists only to thwart instantiation
   }
   public static synchronized Singleton getInstance(String classname) {
      if(classname == null) throw new IllegalArgumentException("Illegal classname");
         Singleton singleton = (Singleton)map.get(classname);
      if(singleton != null) {
         logger.info("got singleton from map: " + singleton);
         return singleton;
      }
      if(classname.equals("SingeltonSubclass_One"))
            singleton = new SingletonSubclass_One();         
         else if(classname.equals("SingeltonSubclass_Two"))
            singleton = new SingletonSubclass_Two();
      map.put(classname, singleton);
      logger.info("created singleton: " + singleton);
      return singleton;
   }
   // Assume functionality follows that's attractive to inherit
}

The preceding base class creates subclass instances and stores them in a map. But that base class is high maintenance because you must update its getInstance() method for every subclass. Luckily, we can use reflection to skirt that issue.

Use reflection

Example 9 lists a singleton with a registry that uses reflection to instantiate a particular class's objects. With this implementation, as opposed to Example 8, the Singleton.getInstance() method does not need to update when new subclasses are implemented.

Example 9. Use reflection to instantiate singletons

import java.util.HashMap;
import org.apache.log4j.Logger;
public class Singleton {
   private static HashMap map = new HashMap();
   private static Logger logger = Logger.getRootLogger();
   protected Singleton() {
      // Exists only to thwart instantiation
   }
   public static synchronized Singleton getInstance(String classname) {
      Singleton singleton = (Singleton)map.get(classname);
      if(singleton != null) {
         logger.info("got singleton from map: " + singleton);
         return singleton;
      }
      try {
         singleton = (Singleton)Class.forName(classname).newInstance();
      }
      catch(ClassNotFoundException cnf) {
         logger.fatal("Couldn't find class " + classname);    
      }
      catch(InstantiationException ie) {
         logger.fatal("Couldn't instantiate an object of type " + classname);    
      }
      catch(IllegalAccessException ia) {
         logger.fatal("Couldn't access class " + classname);    
      }
      map.put(classname, singleton);
      logger.info("created singleton: " + singleton);
      return singleton;
   }
}

One more thing concerning singleton registries: they should be encapsulated in their own class for maximum reuse.

Example 10. A SingletonRegistry class

import java.util.HashMap;
import org.apache.log4j.Logger;
public class SingletonRegistry {
   public static SingletonRegistry REGISTRY = new SingletonRegistry();
   private static HashMap map = new HashMap();
   private static Logger logger = Logger.getRootLogger();
   protected SingletonRegistry() {
      // Exists to defeat instantiation
   }
   public static synchronized Object getInstance(String classname) {
      Object singleton = map.get(classname);
      if(singleton != null) {
         return singleton;
      }
      try {
         singleton = Class.forName(classname).newInstance();
         logger.info("created singleton: " + singleton);
      }
      catch(ClassNotFoundException cnf) {
         logger.fatal("Couldn't find class " + classname);    
      }
      catch(InstantiationException ie) {
         logger.fatal("Couldn't instantiate an object of type " + 
                       classname);    
      }
      catch(IllegalAccessException ia) {
         logger.fatal("Couldn't access class " + classname);    
      }
      map.put(classname, singleton);
      return singleton;
   }
}

Notice I implemented the SingletonRegistry class as a singleton. I also generalized the registry so it can store and retrieve any type of object. Example 11 shows a Singleton class that uses the registry:

Example 11. A Singleton class that uses the registry

import java.util.HashMap;
import org.apache.log4j.Logger;
public class Singleton {
   protected Singleton() {
      // Exists only to thwart instantiation.
   }
   public static Singleton getInstance() {
      return (Singleton)SingletonRegistry.REGISTRY.getInstance(classname);
   }
}

The preceding Singleton class uses the registry's singleton instance to retrieve singleton objects by class name.

Now that we've seen how to implement thread-safe singletons and how to use a registry to specify singleton class names at runtime, let's examine how to deal with classloaders and serialization.

Classloaders

Because multiple classloaders are commonly used in many situations—including servlet containers—you can wind up with multiple singleton instances no matter how carefully you've implemented your singleton classes. If you want to make sure the same classloader loads your singletons, you must specify the classloader yourself; for example:

private static Class getClass(String classname) 
                                         throws ClassNotFoundException {
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      if(classLoader == null)
         classLoader = Singleton.class.getClassLoader();
      return (classLoader.loadClass(classname));
   }
}

The preceding method tries to associate the classloader with the current thread; if that classloader is null, the method uses the same classloader that loaded a singleton base class. The preceding method can be used instead of Class.forName().

Serialization

If you serialize a singleton and then deserialize it twice, you will have two instances of your singleton, unless you implement the readResolve() method, like this:

Example 12. A serializable singleton

import org.apache.log4j.Logger;
public class Singleton implements java.io.Serializable {
   public static Singleton INSTANCE = new Singleton();
   protected Singleton() {
      // Exists only to thwart instantiation.
   }
      private Object readResolve() {
            return INSTANCE;
      }
}

The previous singleton implementation returns the lone singleton instance from the readResolve() method; therefore, whenever the Singleton class is deserialized, it will return the same singleton instance.

Example 13 tests Example 12's singleton:

1 2 3 Page 2
Page 2 of 3
How to choose a low-code development platform