By David Geary
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 using the Singleton pattern. He concludes with a look at implementing singleton registries in order to specify singletons at runtime.
Sometimes it's appropriate to have exactly one instance of a class: window managers, print spoolers, and filesystems are prototypical examples. Typically, those types of objects—known as singletons—are accessed by disparate objects throughout a software system, and therefore require a global point of access. Of course, just when you're certain you will never need more than one instance, it's a good bet you'll change your mind.
The Singleton design pattern addresses all of these concerns. With the Singleton design pattern you can:
- Ensure that only one instance of a class is created
- Provide a global point of access to the object
- Allow multiple instances in the future without affecting a singleton class's clients
Although the Singleton design pattern—as evidenced below by the figure below—is one of the simplest design patterns, it presents a number of pitfalls for the unwary Java developer. This article discusses the Singleton design pattern and addresses those pitfalls.
More about Java design patterns
You can read all of David Geary's Java Design Patterns columns, or view a listing of JavaWorld's most recent articles about Java design patterns. See "Design patterns, the big picture" for a discussion about the pros and cons of using the Gang of Four patterns. Want more? Get the Enterprise Java newsletter delivered to your inbox.
The Singleton pattern
In Design Patterns: Elements of Reusable Object-Oriented Software, the Gang of Four describe the Singleton pattern like this:
Ensure a class has only one instance, and provide a global point of access to it.
The figure below illustrates the Singleton design pattern class diagram.
Singleton class diagram
As you can see, there's not a whole lot to the Singleton design pattern. Singletons maintain a static reference to the sole singleton instance and return a reference to that instance from a static instance()
method.
Example 1 shows a classic Singleton design pattern implementation:
Example 1. The classic singleton
public class ClassicSingleton { private static ClassicSingleton instance = null; protected ClassicSingleton() { // Exists only to defeat instantiation. } public static ClassicSingleton getInstance() { if(instance == null) { instance = new ClassicSingleton(); } return instance; } }
The singleton implemented in Example 1 is easy to understand. The ClassicSingleton
class maintains a static reference to the lone singleton instance and returns that reference from the static getInstance()
method.
There are several interesting points concerning theClassicSingleton
class. First, ClassicSingleton
employs a technique known as lazy instantiation to create the singleton; as a result, the singleton instance is not created until the getInstance()
method is called for the first time. This technique ensures that singleton instances are created only when needed.
Second, notice that ClassicSingleton
implements a protected constructor so clients cannot instantiate ClassicSingleton
instances; however, you may be surprised to discover that the following code is perfectly legal:
public class SingletonInstantiator { public SingletonInstantiator() { ClassicSingleton instance = ClassicSingleton.getInstance(); ClassicSingleton anotherInstance = new ClassicSingleton(); ... } }
How can the class in the preceding code fragment—which does not extendClassicSingleton
—create a ClassicSingleton
instance if the ClassicSingleton
constructor is protected? The answer is that protected constructors can be called by subclasses and by other classes in the same package. Because ClassicSingleton
andSingletonInstantiator
are in the same package (the default package),SingletonInstantiator()
methods can create ClassicSingleton
instances. This dilemma has two solutions: You can make the ClassicSingleton
constructor private so that onlyClassicSingleton()
methods call it; however, that means ClassicSingleton
cannot be subclassed. Sometimes, that is a desirable solution; if so, it's a good idea to declare your singleton class final
, which makes that intention explicit and allows the compiler to apply performance optimizations. The other solution is to put your singleton class in an explicit package, so classes in other packages (including the default package) cannot instantiate singleton instances.
A third interesting point about ClassicSingleton
: it's possible to have multiple singleton instances if classes loaded by different classloaders access a singleton. That scenario is not so far-fetched; for example, some servlet containers use distinct classloaders for each servlet, so if two servlets access a singleton, they will each have their own instance.
Fourth, if ClassicSingleton
implements thejava.io.Serializable
interface, the class's instances can be serialized and deserialized. However, if you serialize a singleton object and subsequently deserialize that object more than once, you will have multiple singleton instances.
Finally, and perhaps most important, Example 1's ClassicSingleton
class is not thread-safe. If two threads—we'll call them Thread 1 and Thread 2—callClassicSingleton.getInstance()
at the same time, two ClassicSingleton
instances can be created if Thread 1 is preempted just after it enters the if
block and control is subsequently given to Thread 2.
As you can see from the preceding discussion, although the Singleton pattern is one of the simplest design patterns, implementing it in Java is anything but simple. The rest of this article addresses Java-specific considerations for the Singleton pattern, but first let's take a short detour to see how you can test your singleton classes.
Test singletons
Throughout the rest of this article, I use JUnit in concert with log4j to test singleton classes. If you are not familiar with JUnit or log4j, see Resources.
Example 2 lists a JUnit test case that tests Example 1's singleton:
Example 2. A singleton test case
import org.apache.log4j.Logger; import junit.framework.Assert; import junit.framework.TestCase; public class SingletonTest extends TestCase { private ClassicSingleton sone = null, stwo = null; private static Logger logger = Logger.getRootLogger(); public SingletonTest(String name) { super(name); } public void setUp() { logger.info("getting singleton..."); sone = ClassicSingleton.getInstance(); logger.info("...got singleton: " + sone); logger.info("getting singleton..."); stwo = ClassicSingleton.getInstance(); logger.info("...got singleton: " + stwo); } public void testUnique() { logger.info("checking singletons for equality"); Assert.assertEquals(true, sone == stwo); } }
Example 2's test case invokes ClassicSingleton.getInstance()
twice and stores the returned references in member variables. The testUnique()
method checks to see that the references are identical. Example 3 shows that test case output:
Example 3. Test case output
Buildfile: build.xml init: [echo] Build 20030414 (14-04-2003 03:08) compile: run-test-text: [java] .INFO main: getting singleton... [java] INFO main: created singleton: Singleton@e86f41 [java] INFO main: ...got singleton: Singleton@e86f41 [java] INFO main: getting singleton... [java] INFO main: ...got singleton: Singleton@e86f41 [java] INFO main: checking singletons for equality [java] Time: 0.032 [java] OK (1 test)
As the preceding listing illustrates, Example 2's simple test passes with flying colors—the two singleton references obtained with ClassicSingleton.getInstance()
are indeed identical; however, those references were obtained in a single thread. The next section stress-tests our singleton class with multiple threads.
Multithreading considerations
Example 1's ClassicSingleton.getInstance()
method is not thread-safe because of the following code:
1: if(instance == null) { 2: instance = new Singleton(); 3: }
If a thread is preempted at Line 2 before the assignment is made, the instance
member variable will still be null
, and another thread can subsequently enter the if
block. In that case, two distinct singleton instances will be created. Unfortunately, that scenario rarely occurs and is therefore difficult to produce during testing. To illustrate this thread Russian roulette, I've forced the issue by reimplementing Example 1's class. Example 4 shows the revised singleton class:
Example 4. Stack the deck
import org.apache.log4j.Logger; public class Singleton { private static Singleton singleton = null; private static Logger logger = Logger.getRootLogger(); private static boolean firstThread = true; protected Singleton() { // Exists only to defeat instantiation. } public static Singleton getInstance() { if(singleton == null) { simulateRandomActivity(); singleton = new Singleton(); } logger.info("created singleton: " + singleton); return singleton; } private static void simulateRandomActivity() { try { if(firstThread) { firstThread = false; logger.info("sleeping..."); // This nap should give the second thread enough time // to get by the first thread. Thread.currentThread().sleep(50); } } catch(InterruptedException ex) { logger.warn("Sleep interrupted"); } } }
Example 4's singleton resembles Example 1's class, except the singleton in the preceding listing stacks the deck to force a multithreading error. The first time thegetInstance()
method is called, the thread that invoked the method sleeps for 50 milliseconds, which gives another thread time to call getInstance()
and create a new singleton instance. When the sleeping thread awakes, it also creates a new singleton instance, and we have two singleton instances. Although Example 4's class is contrived, it stimulates the real-world situation where the first thread that calls getInstance()
gets preempted.
Example 5 tests Example 4's singleton:
Example 5. A test that fails
import org.apache.log4j.Logger; import junit.framework.Assert; import junit.framework.TestCase; public class SingletonTest extends TestCase { private static Logger logger = Logger.getRootLogger(); private static Singleton singleton = null; public SingletonTest(String name) { super(name); } public void setUp() { singleton = null; } public void testUnique() throws InterruptedException { // Both threads call Singleton.getInstance(). Thread threadOne = new Thread(new SingletonTestRunnable()), threadTwo = new Thread(new SingletonTestRunnable()); threadOne.start(); threadTwo.start(); threadOne.join(); threadTwo.join(); } private static class SingletonTestRunnable implements Runnable { public void run() { // Get a reference to the singleton. Singleton s = Singleton.getInstance(); // Protect singleton member variable from // multithreaded access. synchronized(SingletonTest.class) { if(singleton == null) // If local reference is null... singleton = s; // ...set it to the singleton } // Local reference must be equal to the one and // only instance of Singleton; otherwise, we have two // Singleton instances. Assert.assertEquals(true, s == singleton); } } }
Example 5's test case creates two threads, starts each one, and waits for them to finish. The test case maintains a static reference to a singleton instance, and each thread callsSingleton.getInstance()
. If the static member variable has not been set, the first thread sets it to the singleton obtained with the call to getInstance()
, and the static member variable is compared to the local variable for equality.
Here's what happens when the test case runs: The first thread calls getInstance()
, enters the if
block, and sleeps. Subsequently, the second thread also calls getInstance()
and creates a singleton instance. The second thread then sets the static member variable to the instance it created. The second thread checks the static member variable and the local copy for equality, and the test passes. When the first thread awakes, it also creates a singleton instance, but that thread does not set the static member variable (because the second thread has already set it), so the static variable and the local variable are out of synch, and the test for equality fails. Example 6 lists Example 5's test case output:
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 thegetInstance()
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]Build20030414(14-04-200303:15)
compile:[javac]Compiling2 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 thegetInstance()
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 thesingleton
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 thesingleton
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 agetInstance()
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 synchronizedgetInstance()
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, theSingleton.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 isnull
, the method uses the same classloader that loaded a singleton base class. The preceding method can be used instead ofClass.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 thereadResolve()
method; therefore, whenever the Singleton
class is deserialized, it will return the same singleton instance.
Example 13 tests Example 12's singleton:
Example 13. Test a serializable singleton
import java.io.*; import org.apache.log4j.Logger; import junit.framework.Assert; import junit.framework.TestCase; public class SingletonTest extends TestCase { private Singleton sone = null, stwo = null; private static Logger logger = Logger.getRootLogger(); public SingletonTest(String name) { super(name); } public void setUp() { sone = Singleton.INSTANCE; stwo = Singleton.INSTANCE; } public void testSerialize() { logger.info("testing singleton serialization..."); writeSingleton(); Singleton s1 = readSingleton(); Singleton s2 = readSingleton(); Assert.assertEquals(true, s1 == s2); } private void writeSingleton() { try { FileOutputStream fos = new FileOutputStream("serializedSingleton"); ObjectOutputStream oos = new ObjectOutputStream(fos); Singleton s = Singleton.INSTANCE; oos.writeObject(Singleton.INSTANCE); oos.flush(); } catch(NotSerializableException se) { logger.fatal("Not Serializable Exception: " + se.getMessage()); } catch(IOException iox) { logger.fatal("IO Exception: " + iox.getMessage()); } } private Singleton readSingleton() { Singleton s = null; try { FileInputStream fis = new FileInputStream("serializedSingleton"); ObjectInputStream ois = new ObjectInputStream(fis); s = (Singleton)ois.readObject(); } catch(ClassNotFoundException cnf) { logger.fatal("Class Not Found Exception: " + cnf.getMessage()); } catch(NotSerializableException se) { logger.fatal("Not Serializable Exception: " + se.getMessage()); } catch(IOException iox) { logger.fatal("IO Exception: " + iox.getMessage()); } return s; } public void testUnique() { logger.info("testing singleton uniqueness..."); Singleton another = new Singleton(); logger.info("checking singletons for equality"); Assert.assertEquals(true, sone == stwo); } }
The preceeding test case serializes Example 12's singleton and deserializes it twice. Then the test case checks to see if the deserialized singletons are the same object. Here's the test case output:
Buildfile: build.xml init: [echo] Build 20030422 (22-04-2003 11:32) compile: run-test-text: [java] .INFO main: testing singleton serialization... [java] .INFO main: testing singleton uniqueness... [java] INFO main: checking singletons for equality [java] Time: 0.1 [java] OK (2 tests)
Singleton sign-off
The Singleton pattern is deceivingly simple, especially for Java developers. In this article, I've demonstrated how Java developers implement singletons, considering multithreading, classloaders, and serialization. I've also shown how you can implement singleton registries that let you specify singleton classes at runtime.
David Geary is the author of Core JSTL Mastering the JSP Standard Tag Library (Prentice Hall, 2002; ISBN: 0131001531), Advanced JavaServer Pages (Prentice Hall, 2001; ISBN: 0130307041), and the Graphic Java series (Prentice Hall). David has been developing object-oriented software with numerous object-oriented languages for 18 years. Since the GOF Design Patterns book was published in 1994, David has been an active proponent of design patterns, and has used and implemented design patterns in Smalltalk, C++, and Java. In 1997, David began working full-time as an author and occasional speaker and consultant. David is a member of the expert groups defining the JSP Standard Tag Library and JavaServer Faces, and is a contributor to the Apache Struts JSP framework.
Learn more about this topic
- Download this article's source code
http://www.javaworld.com/javaworld/jw-04-2003/designpatterns/jw-0425-designpatterns.jar - The famous Gang of Four book Design Patterns: Elements of Reusable Object-Oriented Software (Eric Gamma, Richard Helm, Ralph Johnson, John Vlissides, Addison-Wesley Publishing Co., 1995; ISBN0201633612)
http://www.amazon.com/exec/obidos/ASIN/0201633612/javaworld - JUnit
http://www.junit.org - log4j
http://jakarta.apache.org/log4j/docs/index.html - Designing Object-Oriented Software, Rebecca Wirfs-Brock, Brain Wilkerson, and Lauren Wiener (Prentice-Hall, 1990; ISBN0136298257)
http://www.amazon.com/exec/obidos/ASIN/0136298257/javaworld - Object-Oriented Analysis and Design With Applications, Grady Booch (Addison-Wesley Publishing Co., 1994; ISBN0805353402)
http://www.amazon.com/exec/obidos/ASIN/0805353402/javaworld - Object-Oriented Software Construction, Second Edition, Bertrand Meyer (Prentice-Hall, 2000; ISBN0136291554)
http://www.amazon.com/exec/obidos/ASIN/0136291554/javaworld - Read David Geary's previous Java Design Patterns columns
http://www.javaworld.com/columns/jw-java-design-patterns-index.shtml - For more design pattern articles, visit the Design Patterns section of JavaWorld's Topical Index
http://www.javaworld.com/channel_content/jw-patterns-index.shtml - Visit JavaWorld's Programming Theory & Practice discussion
http://forums.devworld.com/webx?50@@.ee6b806 - Sign up for JavaWorld's free weekly email newsletters
http://www.javaworld.com/subscribe - You'll find a wealth of IT-related articles from our sister publications at IDG.net
微信公众号: 架构师日常笔记 欢迎关注!