import java.util.*;
class Singleton
{
private static Singleton instance;
private Vector v;
private boolean inUse;
private Singleton()
{
v = new Vector();
v.addElement(new Object());
inUse = true;
}
public static Singleton getInstance()
{
if (instance == null) //1
instance = new Singleton(); //2
return instance; //3
}
}
It may return 2 instances in multi-thread environment. See the process below
- Thread 1 calls the getInstance() method and determines that instance is null at //1.
- Thread 1 enters the if block, but is preempted by thread 2 before executing the line at //2.
- Thread 2 calls the getInstance() method and determines that instance is null at //1.
- Thread 2 enters the if block and creates a new Singleton object and assigns the variable instance to this new object at //2.
- Thread 2 returns the Singleton object reference at //3.
- Thread 2 is preempted by thread 1.
- Thread 1 starts where it left off and executes line //2 which results in another Singleton object being created.
- Thread 1 returns this object at //3.
There are two ways to avoid this.
- Synchronization of a getInstance().
- Use a static field.
Here is the smaple of Synchronization:
public static synchronized Singleton getInstance()
{
if (instance == null) //1
instance = new Singleton(); //2
return instance; //3
}
Here is the sample of static:
class Singleton
{
private Vector v;
private boolean inUse;
private static Singleton instance = new Singleton();
private Singleton()
{
v = new Vector();
inUse = true;
//...
}
public static Singleton getInstance()
{
return instance;
}
}
Java programmers created the double-checked locking idiom to be used with the Singleton creation pattern to limit how much code is synchronized. However, due to some little-known details of the Java memory model, this double-checked locking idiom is not guaranteed to work.
Here is an example of "double-checked locking"
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
inst = new Singleton(); //4
}
instance = inst; //5
}
}
}
return instance;
}
- Thread 1 enters the getInstance() method.
- Because instance is null, thread 1 enters the first synchronized block at //1.
- The local variable inst gets the value of instance, which is null at //2.
- Because inst is null, thread 1 enters the second synchronized block at //3.
- Thread 1 then begins to execute the code at //4, making inst non-null but before the constructor for Singleton executes. (It is not null but the instance is not fully initialized. This is the out-of-order write problem.)
- Thread 1 is preempted by Thread 2.
- Thread 2 enters the getInstance() method.
- Because instance is null, thread 2 attempts to enter the first synchronized block at //1. Because thread 1 currently holds this lock, thread 2 blocks.
- Thread 1 then completes its execution of //4.
- Thread 1 then assigns a fully constructed Singleton object to the variable instance at //5 and exits both synchronized blocks.
- Thread 1 returns instance.
- Thread 2 then executes and assigns instance to inst at //2.
- Thread 2 sees that instance is non-null, and returns it.
It may not work because the compiler may optimize the code like the code below which may still return two different instances.
synchronized(Singleton.class) { //3
//inst = new Singleton(); //4
instance = new Singleton();
}
//instance = inst; //5
For more information, please refer to this article
No comments:
Post a Comment