As anatolyg pointed out, you should make the field singleton
private to avoid unwanted non-thread-safe accesses to that field.
Furthermore, even though in:
public static Singleton get(){
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
return singleton;
}
the return singleton;
is outside of the synchronized
block, this code is still thread-safe because the remaining code is inside the synchronized
block, and consequently, all threads within that block will force a happens-before relation (i.e., there is no way threads will return null if the instance was properly set).
That being said beware that: quoting Holger
As long as the actual write to singleton happens-before the beginning
of the synchronized block, everything works. It only works
because there is at most one write, definitely performed before the
return. If more writes were possible, they could happen concurrently
to the return statement that is outside the synchronized block.
There is a complete SO Thread that tackles why it is thread-safe to leave the return singleton
outside the synchronized
block.
Notwithstanding, I share the same opinion of others users such as this one
since the return doesn't take any CPU or anything, there is no reason
why it shouldn't be inside of the synchronized block. If it was then
the method can be marked as synchronized if we are in the Singleton
class here. This would be cleaner and better in case singleton gets
modified elsewhere.
That being said you don't need the volatile clause because you are synchronizing the initialization of the variable singleton
. In this context, the synchronized
clause guarantees not only that multiple threads will not access:
if (singleton == null) {
singleton = new Singleton();
}
but also that each thread sees the most up-to-date reference of the singleton
field. Consequently, the race-condition of multiple threads assigning different object instances to the singleton
field will not happen.
Some one say that no volatile for singleton variable is wrong.
Probably this person is mistaken your code with the double-checked locking pattern, which is a performance optimization over the version that you have shown. In your version threads will synchronize every time they call the method get
, which is not necessary after the variable singleton
has been correctly initialized. This is the overhead that the double-checked locking pattern tries to avoid. To achieve that the volatile is needed (you can read an in-depth explanation on this SO Thread), more information about this double-checked locking pattern can be found here.