package singleton;
public class SingletonClass {
private static SingletonClass singleton = null;
private SingletonClass() {
}
static boolean stopThread = true;
//approach 1 which fails in multithereaded env
/*public static SingletonClass getInstance(){
if(null == singleton){
try {
if(stopThread){
stopThread = false;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton = new SingletonClass();
}
return singleton;
}*/
//approach 2 which works
//method is synchronized
/* public static synchronized SingletonClass getInstance(){
if(null == singleton){
try {
if(stopThread){
stopThread = false;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton = new SingletonClass();
}
return singleton;
}*/
***//approach 3 which is failing but I don't understand why
//big block of code is synchronized
public static SingletonClass getInstance(){
if(null == singleton){
synchronized (SingletonClass.class){
try {
if(stopThread){
stopThread = false;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton = new SingletonClass();
}
}
return singleton;
}***
//small block of code is synchronized, checked null again because even object instantiation is synchronised
//if we don't check null, it will create new object once again
//approach 4 which works
/* public static SingletonClass getInstance(){
if(null == singleton){
try {
if(stopThread){
System.out.println("in thread...");
stopThread = false;
//even if we interchange above 2 lines it makes whole lot of difference
//till the time it takes to print "in thread"
//2nd thread reaches there n enters if(stopThread) block because
//stopThread is still true because 1st thread spent time in writing that sentence and
//did not set stopThread = false by the time 2nd thread reached there
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (SingletonClass.class){
System.out.println("in this block");
if(null == singleton){
singleton = new SingletonClass();
}
}
}
return singleton;
}*/
}
---------------------------------------------------------
package singleton;
public class ThreadUsage implements Runnable {
@Override
public void run() {
SingletonClass singletonOne = SingletonClass.getInstance();
System.out.println(singletonOne.hashCode());
}
}
----------------------------------------------------------------
package singleton;
class ThreadUsageTest {
public static void main(String[] args) {
Runnable runnableOne = new ThreadUsage();
Runnable runnableTwo = new ThreadUsage();
new Thread(runnableOne).start();
new Thread(runnableTwo).start();
}
}
---------------------------------------------------------------------------
In approach 3, it's not giving same hashCode for 2 objects, I've kept both Thread.sleep as well as object instantiation under synchronised block so what I'm thinking is , 2nd thread should not even enter this block until 1st finishes, but it's still doing and creating 2nd object leading to diff hashCode. What am I mssing here? Could someone correct my understanding here ? If I check for null b4 object creation then it's working as expected but why would I need to check null again here because my entire code is under synchronised block?
if(null == singleton)
singleton = new SingletonClass();
Here's one way that code (approach 3) ends up creating and returning two (or more) separate objects for the singleton:
- Thread A enters the function and sees
null
forsingleton
- Thread B enters the function and sees
null
forsingleton
- Thread A enters the synchronized block
- Thread B waits because it can't enter the synchronized block
- Thread A assigns to
singleton
- Thread A exits the synchronized block
- Thread A returns one object
- Thread B enters the synchronized block
- Thread B assigns to
singleton
- Thread B returns a different object
E.g., there's a gap between the null
check and entering the synchronized block that follows it.
To solve it, just make getInstance
a synchronized
method and remove the synchronized
block inside it:
public static synchronized SingletonClass getInstance() {
if (instance == null) {
singleton = new SingletonClass();
}
return singleton;
}
Or if you really want to avoid synchronization on subsequent calls, on Java 5 or later (which hopefully you're using!), declare singleton
volatile
and check again within the synchronized
block:
private static volatile SingletonClass singleton;
// ...
public static SingletonClass getInstance() { // Only works reliably on Java 5 (aka 1.5) and later!
SingletonClass instance = singleton;
if (instance == null) {
synchronized (SingletonClass.class) {
instance = singleton;
if (instance == null) {
singleton = instance = new SingletonClass();
}
}
}
return instance;
}
That's the double-checked locking idiom. In Java 4 (aka 1.4) and earlier that wasn't necessarily reliable, but it is now (provided you use volatile
on the member).
In a comment user2683814 asked a good question:
Could you explain the assignment to local variable before null check in the second code snippet ? Checking the class variable directly won’t work ?
Yes, it would work, but less efficiently.
In cases where singleton
is non-null
, using a local means the method only accesses singleton
once. If the code didn't use a local, it would access singleton
at least twice (once to check it, once to return it). Since accessing a volatile
variable is slightly expensive, better to use the local (which in the code above can be optimized into a register).
That may seem like premature micro-optimization, but if you weren't doing this in performance-critical code, you'd just make the method synchronized
and avoid the complexity of double-checked locking entirely. :-)