javamultithreadingconcurrencyjava.util.concurrentdouble-checked-locking

Convert double check locking from using synchronized to locks in JAVA


Consider the following code implementing double check locking using the synchronized keyword in JAVA 8:

private static void redoHeavyInitialisation() {
    if (needToReinitialise()) {
        synchronized (MyClass.class) {
            if (needToReinitialise()) {
                doHeavyInitialisation();
            }
        }
    }
}

The reason double check locking is used is because the initialisation is heavy (hence lazy) AND it can happen more than once (hence singleton pattern can not be used, correct me if I am wrong).

Anyway, first, how do you convert the code above to use Lock from the JAVA concurrent package instead of using synchronized keyword?

Only after that AND optionally, feel free to comment on using Lock or synchronized keyword which one is better.

Remember, this question is not about Lock vs synchronized comparison. Answer attempts without answering the code conversion part will not be picked as accepted answer.


Solution

  • Transformation of synchronized blocks to the equivalent block using ReentrantLock is pretty rote.

    First you create a lock with the same or similar scope and lifetime as the object you were locking on. Here you are locking on MyClass.class, hence a static lock, so you can map this to a static lock in MyClass, such as MyClass.initLock.

    Then just replace each:

    synchronized (object) {
    

    with

    lock.lock();
    try {
    

    and each associated closing brace with

    } finally {
      lock.unlock();
    }
    

    Putting it all together you have:

    private final static ReentrantLock initLock = new ReentrantLock();
    
    private static void redoHeavyInitialisation() {
        if (needToReinitialise()) {
            MyClass.initLock.lock();
            try {
                if (needToReinitialise()) {
                    doHeavyInitialisation();
                }
            } finally {
              MyClass.initLock.unlock();
            }
        }
    }
    

    Performance-wise there is little daylight between the approaches. They essentially have the same semantics and usually use similar underlying mechanisms. In the past, there have been performance differences - sometimes optimizations have gone in that affect one or the other, so on some JVMs you can find a difference, but the whole point of double checked locking is to avoid taking the lock anyway, so just do what's simplest. You only get the lock for a very small transitory period while the needToReinitialise() method is running, so the locking cost won't have any ongoing impact.