您的位置:首页 > 其它

Singleton pattern and problem with double checked locking

2009-02-09 14:58 573 查看

http://javabeans.asia/2008/09/06/singleton_pattern_and_problem_with_double_checked_locking.html

Comparison of singleton initialization idioms

There are several ways to initialize singleton object. Some are thread safe and some are not. Until the last two days I thought that double-checked locking idiom offers the best solution for singleton initialization in multi threaded environment. Well its not.

To begin, I want to describe briefly several ways to initialize singleton:

Lazy initialization

Eager initialization

DCL

Class holder lazy initialization

Enum singleton

Lazy initialization
The goal of lazy initialization was to defer initialization of an object until it is actually needed, while making sure that initialization is done only once. Lazy initialization example illustrated below:

view plaincopy to clipboardprint?

public class Singleton

{

private static Singleton instance = null;

private Singleton() {

}

public static Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

public class Singleton
{
private static Singleton instance = null;

private Singleton() {
}

public static Singleton getInstance() {
if (instance == null)  {
instance = new Singleton();
}
return instance;
}
}

This will work fine in single-threaded environment. Unfortunately since getInstance() is not synchronized, two different instances of the object can be returned if two threads will access getInstance() method concurrently. Which makes this lazy initialization unsafe in multi threaded environment.

Unsafe lazy initialization can be fixed by making getInstance() synchronized:

view plaincopy to clipboardprint?

public class Singleton

{

private static Singleton instance = null;

private Singleton() {

}

public static synchronized Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

public class Singleton
{
private static Singleton instance = null;

private Singleton() {
}

public static synchronized Singleton getInstance() {
if (instance == null)  {
instance = new Singleton();
}
return instance;
}
}

However, in this case there is performance hit for every invocation of getInstance() method. Synchronized methods runs very slow compared to unsynchronized methods. I haven't tested this issue my self, so i cannot tell by how much slower.

Eager initialization
By using eager initialization, the synchronization cost incurred for each method call is eliminated:

view plaincopy to clipboardprint?

public class Singleton

{

private static Singleton instance = new Singleton();

private Singleton() {

}

public static Singleton getInstance() {

return instance;

}

}

public class Singleton
{
private static Singleton instance = new Singleton();

private Singleton() {
}

public static Singleton getInstance() {
return instance;
}
}

The problem with eager initialization that Singleton object is instantiated regardless whether it is going to be used or not.

DCL
Double-checked locking (DCL) idiom was created to allow lazy initialization of a singleton object, without performance reducing costs as a result of synchronization. Unfortunately it does not work. Lets have a look why:

view plaincopy to clipboardprint?

public class Singleton {

private static Singleton instance = null;

private Singleton() {

}

public static Singleton getInstance() {

if (instance == null) {

synchronized(Singleton.class) {

if (instance == null) {

instance = new Singleton();

}

}

}

return instance;

}

}

public class Singleton {

private static Singleton instance = null;

private Singleton() {
}

public static Singleton getInstance() {
if (instance == null)  {
synchronized(Singleton.class)  {
if (instance == null)  {
instance = new Singleton();
}
}
}
return instance;
}
}

First there is a check whether initialization is needed with out synchronization, and if the instance is not null - then use it.
Otherwise, synchronize and check again if instance is null by making sure that only one thread will initialize the shared Singleton.

The problem with DCL is that code of fetching reference to constructed Singleton object does not require synchronization.
Lets say that thread 'a' has entered synchronized block, made the instance not null but hasn't executed the constructor yet. At this stage thread 'b' preempts thread 'a'. Thread 'b' checks if the instance is not null, and since its not, it returns reference to a partially constructed Singleton object.

In other words - thread 'b' sees a current value of the Singleton object reference instead of its stale value, which causes the object to be seen in invalid or incorrect state.

Class holder lazy initialization
Instead of DCL, lazy initialization class holder idiom offers better solution for singleton initialization in multi threaded environment, with the same benefits as DCL:

view plaincopy to clipboardprint?

public class SingletonFactory {

private static class SingletonHolder {

public static Singleton instance = new Singleton();

}

public static Singleton getInstance() {

return SingletonHolder.instance;

}

}

public class SingletonFactory {

private static class SingletonHolder {
public static Singleton instance = new Singleton();
}

public static Singleton getInstance()  {
return SingletonHolder.instance;
}
}

The SingletonHolder class initialization is deferred until its actually used, and because Singleton is initialized with static initializer, no additional synchronization is needed.
The first invocation of getInstance() by any thread causes SingletonHolder to be loaded and initialized during which time the Singleton is initialized through static initializer.

Enum singleton
Joshua Bloch in his book "Effective Java" offers a new interesting solution to initialize a Singleton by using Enum with a single type:

view plaincopy to clipboardprint?

public class SingletonEnum {

INSTANCE;

public void someMethod() {

...

}

public void anotherMethod() {

...

}

}

public class SingletonEnum {
INSTANCE;

public void someMethod() {
...
}

public void anotherMethod() {
...
}
}

Joshua claims:
"...a single-element enum type is the best way to implement a singleton..."
Some people may argue against this approach by saying that this is not a class, but an enum - it enumarates plus enums cannot be subclassed. Well in case of a singleton there is no point in subclassing, since singletons have private constructors and singletons dont really ment to be subclassed. Lets have a look if Enum singleton is indeed the best solution so far:

1. Enum singleton is final
2. It is serializable
3. It is a single instance in multithreaded environment
4. Does not allow invokation of private constructors through reflection attacks

I think its quite original solution.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: