There are 4 ways to create a thread-safe singleton in a multithread environment.
- Using a LocalThread
- Using a full synchronized method
- Using a static singleton
- Using DCL (not a safe solution)
- Using volatil keyword
But wich solution is the better ?...
LocalThread
Probably the most complicated solution.
Basicly, LocalThread is an unique attribut wich won't be shared between the thread.
You can see it as a volatil attribute.
This implementation suxs before JVM 1.4 (because LocalThread was a synchronized object).
But with the new JVM, it has a better implementation and is faster.
public class LocaThreadSingleton extends Singleton {
private static ThreadLocal initHolder = new ThreadLocal();
public static Singleton getInstance () {
if (initHolder.get() == null){
synchronized (LocaThreadSingleton.class){
if (instance== null)
instance = new LocaThreadSingleton();
initHolder.set(Boolean.TRUE);
}
}
return instance;
}
}
The Static solution
This is the best - and the more efficient solution.
You should use it in almost all cases (It doesn't fit if you need to create the Singleton with class attributes/variables).
public class StaticSingleton extends Singleton {
private static StaticSingleton instance = new StaticSingleton() ;
public static Singleton getInstance () {
return instance;
}
}
Full synchronized solution
Probably the worst solution, after DCL : It's totally inefficient because you will synchronized the whole method and lock it every single call.
public class SynchronizedSingleton extends Singleton{
public static synchronized Singleton getInstance () {
if (instance == null)
instance = new SynchronizedSingleton();
return instance ;
}
}
Double Check solution
The worst solution. It just doesn't work because of the out of order memory write.
Basically, the jvm could do the following :
mem = allocate() ;
instance = mem ;
constructorSingleton(instance)
Because of this behaviour, you could get an "instance" object !=null but also != new DCLSingleton();
This may happen in a very exceptional circumstance, but you have to keep in mind that it is not safe.
Here is the wrong implementation, don't use it :
public class DCLSingleton extends Singleton{
public static Singleton getInstance () {
if (instance==null){
synchronized (DCLSingleton.class){
if(instance==null)
instance=new DCLSingleton();
}
}
return instance;
}
}
Volatil solution
Clean and efficient !
public class VolatilSingleton extends Singleton{
private volatile static Singleton singleton;
public static Singleton getInstance () {
if(singleton==null) {
synchronized(Singleton.class){
if(singleton==null)
singleton= new VolatilSingleton();
}
}
return singleton;
}
}
I made a couple of bench, trying to find-out wich solution is the better one.
The bench I made throw at the same moment 5 trheads (that's because I wanted to avoid any kind JVM optimisation due to the fact singletons was created from the same object/thread).
Every single threads will execute X other threads (lets say 50 000) wich will try to get an instance of the type of singleton you have chosen at the start of the bench.
The cost of the singleton creation is +- 500ms (you can choose it)
The first result is that the full solution "synchronized" is really bad.
That's not because of the cost of the "lock" operation (and I think it's important to notice that).
The matter is that too many thread wich are kept in the "queue", even after the instanciacion of your singleton.
So it's clear that :
- DCL is bad (it's not safe)
- full synchronized solution is bad
- Static method is the better solution, when you can implement it.
Now what about volatil vs LocalThread ?
Let's run our bench ! (average of 6time for each solution)
Here are the results.
Volatil seems to be the fastest solution.
LocalThread is slower than volatil, and more complicated.
My point is :
- Use the static solution for all your implementation, because it's easy and safe.
- If you can't or if you really look after optimization and you are sure your application will run with 1.6 Jvm, think about the volatil implementation.
Find bench Source & .jar here
You can monitor your bench using jconsole (you will find it into your jdk).
You need to lauch the jar with the following command :
java -Dcom.sun.management.jmxremote -jar bench.jar
More information about singleton here