Search code examples
javasynchronizationvolatile

Do we really need to copy this.myVolatile locally to do the synchronization double if pattern in Java 8?


Do we get any benefit to change existing code from:

class MyClass {
    volatile Object myVariable;
    Object myMethod() {
        if (myVariable == null) {
            synchronized(this) {
                if (myVariable == null) {
                    myVariable = this.getNewValue();
                }
            }
        }
        return myVariable;
    }
}

to

class MyClass {
    volatile Object myVariable;
    Object myMethod() {
        Object tmp = this.myVariable;
        if (tmp == null) {
            synchronized(this) {
                tmp = this.myVariable;
                if (tmp == null) {
                    this.myVariable = tmp = this.getNewValue();
                }
            }   
        }
        return tmp;
    }
}

I don't see the point of copying this.myVariable locally before using it, and I don't think it's a good practice to use "this." for every class variable.


Solution

  • Copying to a local variable is more efficient and more correct.

    More Efficient: Assume that in the common case, myVariable is non-null. In the first version, you do two reads of myVariable, once to check for null and once to return the value. In the second version, you do one read of myVariable and two reads of tmp (local variable access, which is trivial). The whole point of using volatile is the strong memory guarantees, and those guarantees mean that two reads is a non-trivial performance hit over just one read.

    More Correct: Assume that myVariable is some sort of "cache" which periodically needs to be refreshed. I.e. there is a background thread which periodically sets myVariable to null so that it will be reloaded on the next read. In the first version, you do two separate reads of myVariable. The first read could return non-null, then the "cache refresh" logic runs and sets myVariable to null. The second read (to return the value) then returns null! In the second version, tmp is always the value you tested for null (assuming that getNewValue() never returns null of course).

    Note that using "this." is a stylistic choice in this code and has no bearing on the correctness or performance issues.

    This is largely just expanding on what is stated in https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java .