public class Factory {
private Singleton instance;
public Singleton getInstance() {
Singleton res = instance;
if (res == null) {
synchronized (this) {
res = instance;
if (res == null) {
res = new Singleton();
instance = res;
}
}
}
return res;
}
}
It is almost correct implementation of thread-safe Singleton
. The only problem I see is:
The thread #1
that is initializing the instance
field can published before it will be initialized completely. Now, the second thread can read instance
in a inconsistent state.
But, for my eye it is only problem here. Is it only problem here?
(And we can make instance
volatile).
Important points. You can use the volatile keyword with variables. Using volatile keyword with classes and methods is illegal. It guarantees that value of the volatile variable will always be read from the main memory, not from the local thread cache.
The volatile modifier is used to let the JVM know that a thread accessing the variable must always merge its own private copy of the variable with the master copy in the memory. Accessing a volatile variable synchronizes all the cached copied of the variables in the main memory.
1. The volatile keyword in Java is the only application to a variable and using a volatile keyword with class and method is illegal. 2. volatile keyword in Java guarantees that the value of the volatile variable will always be read from the main memory and not from Thread's local cache.
After some time (yeah it took 2 years, I know), I think I have the proper answer. To take it literally, the answer to this:
But, for my eye it is only problem here. Is it only problem here?
Would be yes. The way you have it right now, callers of getInstance
will never see a null
. But if Singleton
would have fields, there is no guarantee that those fields will be correctly initialized.
Let's take this slow, since the example is beautiful, IMHO. That code you showed does a single (racy) volatile read
:
public class Factory {
private Singleton instance;
public Singleton getInstance() {
Singleton res = instance; // <-- volatile RACY read
if (res == null) {
synchronized (this) {
res = instance; // <-- volatile read under a lock, thus NOT racy
if (res == null) {
res = new Singleton();
instance = res;
}
}
}
return res;
}
}
Usually, the classical "double check locking" has two racy reads of volatile, for example:
public class SafeDCLFactory {
private volatile Singleton instance;
public Singleton get() {
if (instance == null) { // <-- RACY read 1
synchronized(this) {
if (instance == null) { // <-- non-racy read
instance = new Singleton();
}
}
}
return instance; // <-- RACY read 2
}
}
Because those two reads are racy, without volatile, this pattern is broken. You can read how we can break here, for example.
In your case, there is an optimization, that does one less reading of a volatile field. On some platforms this matters, afaik.
The other part of the question is more interesting. What if Singleton
has some fields that we need to set?
static class Singleton {
//setter and getter also
private Object obj;
}
And a factory, where Singleton
is volatile
:
static class Factory {
private volatile Singleton instance;
public Singleton get(Object obj) {
if (instance == null) {
synchronized (this) {
if (instance == null) {
instance = new Singleton();
instance.setObj(obj);
}
}
}
return instance;
}
}
We have a volatile field, we are safe, right? Wrong. The assign of obj
happens after the volatile write, as such there are no guarantees about it. In plain english: this should help you a lot.
The correct way to fix this is to do the volatile write with an already build instance (fully build):
if (instance == null) {
Singleton local = new Singleton();
local.setObj(obj);
instance = local;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With