Introduction
The singleton pattern enforces object instantiation in order to guarantee only one instance of an Object can be created. While the singleton pattern can be advantageous for memory intensive objects it should be used sparingly. Developers also need to take care to ensure their singleton Objects perform as expected in multithreaded environments.
Example Instantiation Problem
Consider the game Super Mario. While playing the game you only ever want one instance of Mario while playing. The same instance of Mario should be used until he is destroyed or successfully rescues the princess (ending the game). An initial approach might look like this:
public class Mario { public enum State { SMALL, BIG, FLOWER_POWER, STAR_POWER, DESTROYED } public State state; public Mario() { state = State.SMALL; } public void destroy() { state = State.DESTROYED; } public void powerup() { if (state == State.SMALL) { state = State.BIG; } } public void takeDamage() { if (state == State.SMALL) { destroy(); } } // ... } |
Multiple instances (or references) of Mario
could exist concurrently. In a multithreaded environment (for example) Mario
might powerup();
and quickly takeDamage();
. With no guaranteed order of operations we might see Mario
be destroyed from a State.BIG
state (in the case that takeDamage();
ran on one thread before powerup();
did).
The Singleton Pattern
The singleton pattern resolves issues like the one described above by circumscribing an Objects instantiation to one and only one instance. The singleton pattern is one of the simplest design patterns and is implemented as such (refactoring our Mario
code example from above):
public class MarioSingleton { // Add a static variable to hold the single instance of Mario private static MarioSingleton mario; // Make the constructor private blocking instantiation private MarioSingleton() { } // Add a static instantiation method public static MarioSingleton getInstance() { if (mario == null) { mario = new Mario(); } return mario; } // ... the rest of the Mario implementation } |
Now only one instance of Mario can ever be instantiated.
Multithreading Issues
Unfortunately the above MarioSingleton
still doesn’t perform properly in a multithreaded environment. In some cases multiple threads could still end up receiving multiple instances of MarioSingleton
. There’s a few options available to resolve these potential threading issues.
Option 1: Synchronization
The Java synchronization
keyword can be applied the the static getInstance()
method. This will ensure only one instance of MarioSingleton
is created across multiple threads. The downside to this option is performance. If MarioSingleton
instantiation code contains expensive operations, synchronized
will have a negative effect on performance otherwise this solution is adequate. Keep in mind synchronized
will be invoked everytime an instance of MarioSingleton
is requested.
public class MarioSingleton { // synchronize the getInstance() method public static synchronized MarioSingleton getInstance() { } } |
Option 2: Eager Instantiation
Another option for non expensive singleton object instantiation is to use eager instantiation. The singleton instance is created by the JVM when the class is loaded. Note; this solution probably won’t work for a game style problem -> but it’s worth mentioning overall.
public class MarioSingleton { private static MarioSingleton mario = new MarioSingleton(); private MarioSingleton() { } private static MarioSingleton getInstance() { return mario; } } |
Option 3: Double checked locking
Double checked locking uses synchronization but blocks these expensive calls from happening multiple times by first checking if a singleton instance exists, performing the synchronize
call, and then checking again if a singleton instance exists (double checking). This operation is overkill for inexpensive object instantiation. Note: requires Java 5+ as the volatile
keyword did not exist in previous Java versions.
public class MarioSingleton { // volatile to ensure multiple threads properly handle the instantiation of the singleton instance private volatile static MarioSingleton mario; private MarioSingleton() { } public static MarioSingleton getInstance() { if (mario == null) { synchronized (MarioSingleton.class) { if (mario == null) { mario = new MarioSingleton(); } } } } } |
Summary
Note this pattern requires Java version 1.2+ (previous versions contained a bug in the garbage collector which would cause these types of static instances to be collected). You should avoid subclassing Singleton Objects as that would violate the pattern (only one instance should exist). The singleton pattern is great pattern for global objects which should only have a single instance such as device drivers, caches, registries, etc.