Search code examples
javamultithreadingvolatilejava-memory-modeldouble-checked-locking

Is double check with final variable working


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)?


Solution

  • 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.