This is brief discussion of the Singleton design pattern (refer to the GOF book,
Design Patterns), how Singleton classes differ
from classes with static methods, and why the ‘Double-locking’ technique should not be used to implement Singletons.
|
Certain conditions may exist in a design that dictate that there should only ever be one instance of a particular
class (or sometimes a limited number of controlled instances).
Usually these classes represent or allow access to a limited resource,
such as a printer, or a property file. In the case of the property file,
if the properties in this file are to be read by many components in the
system, then it would be inefficient if the file is loaded/read each time
a component requests one of the properties from the file – a more efficient
solution would be to read it once and allow access to this shared instance.
Two common solutions to implementing this shared class would be to implement:
- a class with static methods
- a singleton class
|
Static methods (or Class methods) are associated with a particular Class, rather than an instance of a class.
You can invoke a static method without having an instance of the Class. For example, given this class:
public class ClassA
{
public static int returnValue()
{
return 1;
}
}
you can invoke the method like this:
ClassA.returnValue();
In some cases a static method may provide a good solution – they are useful for utility methods that provide
some common operation on some passed arguments that do not change the state or attributes of its own Class.
However there are drawbacks of static methods:
- they are not a good fit in an Object Oriented world (ie no object instance has to exist to invoke the method)
- they wouldn’t provide a good solution if the design changed and we needed a limited number of instances, say 2,
rather than 1.
- static methods can not be overridden by subclasses (explained in the
next section), meaning that it would be more difficult to reuse a class
with static methods in subclasses.
|
Static methods cannot be overridden in subclasses which you could argue
makes them less flexible than inherited methods. It also means that the
functionality of the static method is still accessible in the parent/superclass,
which could lead to the wrong method be invoked in subclasses, and possibly
future maintenance/debugging headaches.
If you create a static method in subclass with the same name and signature
as a static method in the superclass, you are merely creating another
method with the same name – you have not overridden the method in the
superclass, it is just hidden by the static method with the same name
(but possibly a different implementation).
Also, it is not valid to declare an instance method with the same signature
as a static method in the super class – this gives a compile time error.
Overriding and Hiding are discussed in further detail in the
target=”_blank”>Java Language Specification.
An example of this concept is here
|
Assuming we have decided that the singleton pattern is a good solution for our problem, and that using static
methods would not give us the future flexibility that we may need in our solution, how do we implement one? How can we
implement the singleton class so that it is also ‘lazy initialized’ – ie the singleton is only initialized when it
is first requested?
Heres the first example:
public class SingletonA
{
private SingletonA instance;
/**
* Constructor is hidden so uncontrolled instances can not be created
*/
private SingletonA() {}
public static SingletonA getInstance()
{
if(instance == null)
{
//create the singleton instance
instance = new SingletonA();
}
return instance;
}
}
This appears to achieve what we need, however it is clearly not threadsafe.
|
If we need the instantiation of the single instance to be threadsafe then we need to introduce synchronization.
Look at this next example:
public class SingletonB
{
private SingletonB instance;
/**
* Constructor is hidden so uncontrolled instances can not be created
*/
private SingletonB() {}
public static synchronized SingletonB getInstance()
{
if(instance == null)
{
//create the singleton instance
instance = new SingletonB();
}
return instance;
}
}
If we synchronize the whole getInstance() method we solve
the multithreading issue, but now we have created a bottleneck in our
code – requests to obtain the SingletonB instance will execute sequentially
through this method. If there are to be many concurrent requests to this
method to get the Singleton instance, then this may be a performance problem.
The next refinement is to synchronize the least amount of code as possible (which is always good practice).
|
For this refinement we synchronize only the lines of code that we want to be
executed sequentially, in particular the actual instantiation of the Singleton.
public class SingletonC
{
private SingletonC instance;
/**
* Constructor is hidden so uncontrolled instances can not be created
*/
private SingletonC() {}
public static SingletonC getInstance()
{
if(instance == null)
{
synchronized
{
if(instance == null)
{
//create the singleton instance
instance = new SingletonC();
}
}
}
return instance;
}
}
Heres where the ‘double-checked lock’ concept is introduced. The intent
of this is as follows:
- if the singleton instance has not yet been created, attempt to enter the synchronized block
- if another thread is already in the synchronized block then we will be blocked until they leave
- once inside the synchronized block, check if the instance was possibly created
by the last caller of this method (ie while thread ‘A’ was blocked on
the synchronized block by thread ‘B’, thread ‘B’ could have already
executed the instantiation. Therefore we need to check a second time
to make sure we are not performing the instantiation a second (unnecessary)
time when we enter the synchronized block.
- if a thread calls getInstance() and the instance has already been created then it is returned without
entering the synchronized block.
Logically this is correct and will fulfill all our requirements. Unfortunately it is not guaranteed to work. Why?
|
Although the last solution looks good, it has been proven to not work in all
situations [1]. Some reasons behind this are as follows:
- although the line of code
instance = new SingletonC() is only one line of code in the source, as
generated Java bytecode this one statement will be more than one bytecode statement. In otherwords, the instantiation
of a new instance is not an atomic operation – there will be a chance that the variable instance is not
null even before the new instance has been assigned to it. In our code, the check if(instance == null) will
be false and so instance will be returned in an undetermined state. This code is therefore unreliable in
its current format.
- related to the above statement, some Just-In-Time compilers such as Symantec
JIT (and possibly Hotspot?) rearrange bytecode statements as part of
their optimization. Again this means that instance will
be non-null and so the code will return its current undetermined/unexpected
value.
- multiprocessor systems can also reorder statements as they are executed. For this same reason, it is possible that
instance will be non-null and
so the code will return an undetermined/unexpected value.
The reordering of bytecode statements is referred to as out of order
writes and is defined in the Virtual Machine Specification.
Another problem with the double-checked locking is that in some circumstances
the code may work, but in other cases it will fail. The failures will
be sporadic. This is another reason to avoid using double-checked locking,
because it can not be guaranteed to work.
|
According to David Bacon et al in their analysis of double-checked locking
(see here), there is no way to fix this approach so
that it will work reliably, no matter how elaborate the solution. The
only solution is to avoid use double-checked locking completely.
So how do you implement a threadsafe Singleton class in Java? Sometimes the
best solutions are the simplest:
- introduce the instance as a static field (see example below)
- or, accept the (potential) performance drawback and synchronize the
whole method
public class Singleton
{
private static Singleton instance = new Singleton();
/**
* Constructor is hidden so uncontrolled instances can not be created
*/
private Singleton() {}
public static SingletonC getInstance()
{
return instance;
}
}
This avoids the need for synchronization, and the singleton instance will only
be created on its first reference.
|
1 |
The
Double-checked Locking is Broken’ Declaration – David Bacon (IBM
Research) Joshua Bloch (Javasoft), Jeff Bogda, Cliff Click (Hotspot
JVM project), Paul Haahr, Doug Lea, Tom May, Jan-Willem Maessen, John
D. Mitchell (jGuru) Kelvin Nilsen, Bill Pugh, Emin Gun Sirer |
Other Related Articles
|