Reputation: 283
I have class designed for lazy initialization and storing objects which creation is not necessary threadsafe. Here is the code:
class SyncTest {
private static final Object NOT_INITIALIZED = new Object();
private Object object;
/**
* It's guaranteed by outer code that creation of this object is thread safe
* */
public SyncTest() {
object = NOT_INITIALIZED;
}
public Object getObject() {
if (object == NOT_INITIALIZED) {
synchronized (NOT_INITIALIZED) {
if (object == NOT_INITIALIZED) {
final Object tmpRef = createObject();
object = tmpRef;
}
}
}
return object;
}
/**
* Creates some object which initialization is not thread safe
* @return required object or NOT_INITIALIZED
* */
private Object createObject() {
//do some work here
}
}
Here final
variable tmpRef
is used for storing created object before assigning it to checked variable object
. This works in tests but I can't say it's correct for sure and won't be optimsed by compiler.
Can this appraoch be used or object
field must be declared as volatile
?
Also variant with wrapper class was considered where the line
final Object tmpRef = createObject();
must be replaced with this one:
Object tmpRef = new FinalWrapper(createObject()).getVal();
Wrapper class looks like this:
private class FinalWrapper {
private final Object val;
public FinalWrapper(Object val) {
this.val = val;
}
public Object getVal() {
return val;
}
}
Can some of this examples be safely used in multithreaded environment (especially variant with final local field)?
Upvotes: 1
Views: 187
Reputation: 200158
object = NOT_INITIALIZED;
If you envision this as a trick which will avoid the problems of the usual lazy singleton where you would simply have
object = null;
then it is incorrect; your trick didn't win you any thread safety. You cannot beat the standard double-checked idiom with the volatile
variable pointing to the lazily initialized object. So my suggestion would be to get rid of the extra complexity, use null
, and use volatile
.
Answering your comments:
It's guranteed by JMM that calss initialization with only final fields is always threadsafe.
Class initialization is always thread-safe, regardless of the kind of fields. Every usage of the class is guaranteed to see the objects referred by static fields at least as up-to-date as they were at the time that all class init code completed.
Is the same applicable for local final fields?
The object reached by dereferencing a final field will be at least as up-to-date as it was at the time the constructor of the object containing the final field completed. However, in your solution you never even dereference the field, you just check its value. It is strictly equivalent to check for equality to null
as to the value of the NOT_INITIALIZED
constant.
Upvotes: 2
Reputation: 20254
You should mark the object
variable as volatile
to guarantee thread safety, also note that this pattern is only safe in Java 1.5 and later.
This is a tricky piece of code, to quote Joshua Bloch:
The idiom is very fast but also complicated and delicate, so don't be tempted to modify it in any way. Just copy and paste -- normally not a good idea, but appropriate here
Upvotes: 1