Vous êtes sur la page 1sur 8

JAVA DESIGN PATTERNS

By David Geary

About |

Introducing Java beginners to essential design


patterns from the famous Gang of Four
compilation.

HOW-TO

Simply Singleton
Navigate the deceptively simple Singleton pattern
53
JavaWorld | Apr 25, 2003 2:00 AM PT

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
lesystems are prototypical examples. Typically, those types of objectsknown as singletonsare 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 patternas evidenced below by the gure belowis 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 gure 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


publicclassClassicSingleton{
privatestaticClassicSingletoninstance=null;
protectedClassicSingleton(){
//Existsonlytodefeatinstantiation.
}
publicstaticClassicSingletongetInstance(){
if(instance==null){
instance=newClassicSingleton();
}
returninstance;
}
}

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 the ClassicSingleton 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 rst 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:
publicclassSingletonInstantiator{
publicSingletonInstantiator(){
ClassicSingletoninstance=ClassicSingleton.getInstance();
ClassicSingletonanotherInstance=
newClassicSingleton();
...
}
}

How can the class in the preceding code fragmentwhich does not extend ClassicSingletoncreate 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 and SingletonInstantiator 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 only ClassicSingleton() 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

Sign In | Register
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 the java.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 2call ClassicSingleton.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-speci c
considerations for the Singleton pattern, but rst 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


importorg.apache.log4j.Logger;
importjunit.framework.Assert;
importjunit.framework.TestCase;
publicclassSingletonTestextendsTestCase{
privateClassicSingletonsone=null,stwo=null;
privatestaticLoggerlogger=Logger.getRootLogger();
publicSingletonTest(Stringname){
super(name);
}
publicvoidsetUp(){
logger.info("gettingsingleton...");
sone=ClassicSingleton.getInstance();
logger.info("...gotsingleton:"+sone);
logger.info("gettingsingleton...");

stwo=ClassicSingleton.getInstance();

Sign In | Register

logger.info("...gotsingleton:"+stwo);
}
publicvoidtestUnique(){
logger.info("checkingsingletonsforequality");
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]Build20030414(1404200303:08)
compile:
runtesttext:
[java].INFOmain:gettingsingleton...
[java]INFOmain:createdsingleton:Singleton@e86f41
[java]INFOmain:...gotsingleton:Singleton@e86f41
[java]INFOmain:gettingsingleton...
[java]INFOmain:...gotsingleton:Singleton@e86f41
[java]INFOmain:checkingsingletonsforequality
[java]Time:0.032
[java]OK(1test)

As the preceding listing illustrates, Example 2's simple test passes with ying colorsthe 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=newSingleton();
3:}

If a thread is preempted at Line 2 before the assignment is made, the instance member variable will still

Sign In | Register
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 dif cult 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

importorg.apache.log4j.Logger;
publicclassSingleton{
privatestaticSingletonsingleton=null;
privatestaticLoggerlogger=Logger.getRootLogger();
privatestaticbooleanfirstThread=true;
protectedSingleton(){
//Existsonlytodefeatinstantiation.
}
publicstaticSingletongetInstance(){
if(singleton==null){
simulateRandomActivity();
singleton=newSingleton();
}
logger.info("createdsingleton:"+singleton);
returnsingleton;
}
privatestaticvoidsimulateRandomActivity(){
try{
if(firstThread){
firstThread=false;
logger.info("sleeping...");
//Thisnapshouldgivethesecondthreadenoughtime
//togetbythefirstthread.
Thread.currentThread().sleep(50);
}
}
catch(InterruptedExceptionex){
logger.warn("Sleepinterrupted");

Sign In | Register

}
}

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 rst time the getInstance() 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 rst thread that calls getInstance() gets preempted.
Example 5 tests Example 4's singleton:

Example 5. A test that fails

importorg.apache.log4j.Logger;
importjunit.framework.Assert;
importjunit.framework.TestCase;
publicclassSingletonTestextendsTestCase{
privatestaticLoggerlogger=Logger.getRootLogger();
privatestaticSingletonsingleton=null;
publicSingletonTest(Stringname){
super(name);
}
publicvoidsetUp(){
singleton=null;
}
publicvoidtestUnique()throwsInterruptedException{
//BoththreadscallSingleton.getInstance().
ThreadthreadOne=newThread(newSingletonTestRunnable()),
threadTwo=newThread(newSingletonTestRunnable());
threadOne.start();
threadTwo.start();
threadOne.join();
threadTwo.join();
}
privatestaticclassSingletonTestRunnableimplementsRunnable{
publicvoidrun(){
//Getareferencetothesingleton.
Singletons=Singleton.getInstance();
//Protectsingletonmembervariablefrom
//multithreadedaccess.
synchronized(SingletonTest.class){


if(singleton==null)//Iflocalreferenceisnull...

Sign In | Register

singleton=s;//...setittothesingleton
}
//Localreferencemustbeequaltotheoneand
//onlyinstanceofSingleton;otherwise,wehavetwo
//Singletoninstances.
Assert.assertEquals(true,s==singleton);
}
}
}

Example 5's test case creates two threads, starts each one, and waits for them to nish. The test case
maintains a static reference to a singleton instance, and each thread calls Singleton.getInstance(). If the
static member variable has not been set, the rst 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 rst 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 rst 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:

NEXT

View 53 Comments
Copyright 1994 - 2016 JavaWorld, Inc. All rights reserved.

Sign In | Register

Vous aimerez peut-être aussi