Singleton

From HaFrWiki42
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

<<Back to Patterns

Intent

Ensure a class only has one instance, and provide a global point of access to it.

Motivation

It's important for some classes to have exactly one instance. Although there can be many printers in a system, there should be only one printer spooler. There should be only one file system and one window manager. A digital filter will have one A/D converter. An accounting system will be dedicated to serving one company.

How do we ensure that a class has only one instance and that the instance is easily accessible? A global variable makes an object accessible, but it doesn't keep you from instantiating multiple objects.

A better solution is to make the class itself responsible for keeping track of its sole instance. The class can ensure that no other instance can be created (by intercepting requests to create new objects), and it can provide a way to access the instance. This is the Singleton pattern.

Applicability

Use the Singleton pattern when:

  • there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point.
  • when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code.

Structure

Singleton
Image 1: Singleton

Participants

  • Singleton
    • defines an Instance operation that lets clients access its unique instance. Instance is a class operation (that is, a class method in Smalltalk and a static member function in C++ and Java).
    • may be responsible for creating its own unique instance.

Collaborations

Clients access a Singleton instance solely through Singleton's Instance operation.

Consequences

The Singleton pattern has several benefits:

  1. Controlled access to sole instance. Because the Singleton class encapsulates its sole instance, it can have strict control over how and when clients access it.
  2. Reduced name space. The Singleton pattern is an improvement over global variables. It avoids polluting the name space with global variables that store sole instances.
  3. Permits refinement of operations and representation. The Singleton class may be subclassed, and it's easy to configure an application with an instance of this extended class. You can configure the application with an instance of the class you need at run-time.
  4. Permits a variable number of instances. The pattern makes it easy to change your mind and allow more than one instance of the Singleton class. Moreover, you can use the same approach to control the number of instances that the application uses. Only the peration that grants access to the Singleton instance needs to change.
  5. More flexible than class operations. Another way to package a singleton's functionality is to use class operations (that is, static member functions in C++ or class methods in Smalltalk). But both of these language techniques make it hard to change a design to allow more than one instance of a class. Moreover, static member functions in C++ are never virtual, so subclasses can't override them polymorphically.

Implementation

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;
   }
}
Example 1: ClassicSingleton

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 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();
       ...
  }
}
Example 2: Singleton implementation 

How can the class in the preceding code fragment - which does not extend ClassicSingleton - 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 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.
  • 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 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 2—call 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-specific considerations for the Singleton pattern, but first let's take a short detour to see how you can test your singleton classes.

Testing

For testing applications log4j and junit is used.

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 3: Testing with juinit and log4j

Example 3'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 4 shows that test case output:

Buildfile: build.xml

init:
     [echo] Build 20060213 (13-02-2006 19:38)

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)

Example 4: ANT Build files execute output

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

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 5 shows the revised singleton class:

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 5: Revised Singleton version 'Stack the Deck'

Example 5'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 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 5's class is contrived, it stimulates the real-world situation where the first thread that calls getInstance() gets preempted. Example 6 tests Example 5's singleton:

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 6: Testing Revised Singleton

Example 6'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 calls Singleton.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 7 lists Example 6's test case 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: but was:
   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)

Make it thread safe

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

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

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)

Alternative Thread Safe Singleton

public class Singleton {
   public final static Singleton INSTANCE = new Singleton();
   private Singleton() {
         // Exists only to defeat instantiation.
      }
}
Example 9: Simple Thread Safe Singleton

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.

Singleton as registry

Use a singleton registry to:

  • Specify singleton classes at runtime
  • Prevent singleton subclasses from allowing multiple instances
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
}
Example 10: A singleton with a registry

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 11 lists a singleton with a registry that uses reflection to instantiate a particular class's objects. With this implementation, as opposed to Example 9, the Singleton.getInstance() method does not need to update when new subclasses are implemented.

mport 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;
   }
}
 Example 11. Use reflection to instantiate singletons 

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

Encapsulate the registry

Example 12 lists a singleton registry 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;
   }
}
Example 12: A SingletonRegistry class 

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 13 shows 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);
   }
}
Example 13: A Singleton class that uses the registry


Complete Language examples

  • * Github hjmf1954, PHP-Abstract-Factory-Pattern. A complete working demo in PHP no other tools needed. This example implements also the Singleton pattern (Class Util).

See also

Books

  • Design Patterns, Elements of Reusable Object Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, ISBN: 0201633612.
  • Refactoring: Improving the Design of Existing Code, by Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts, ISBN: 0201485672.