Search code examples
javamultithreadingdouble-checked-locking

Why does ConcurrentHashMap work in Double Checked Locking


In the book "Java Concurrency in Practice" is mentioned that the following code is not threadsafe:

@NotThreadSafe
public class DoubleCheckedLocking {

    private static Resource resource;

    public static Resource getInstance(){
        if(resource == null){
            synchronized (DoubleCheckedLocking.class){
                if(resource == null)
                    resource = new Resource();
            }
        }
        return resource;
    }
}

It is not thread safe because because: - one thread can create new instance of Resource - another thread at the same time in the "if" condition can get not empty reference but the object of Resource will not be completly initialized

In this question is similar code. Resources are stored in concurentHashMap and people say that it is threadSafe. Something like this:

public class DoubleCheckedLocking2 {

    private static ConcurrentHashMap<String, ComplexObject> cache = new ConcurrentHashMap<String, ComplexObject>();

    public static ComplexObject getInstance(String key) {
        ComplexObject result = cache.get(key);
        if (result == null) {
            synchronized (DoubleCheckedLocking2.class) {
                ComplexObject currentValue = cache.get(key);
                if (currentValue == null) {
                    result = new ComplexObject();
                    cache.put(key, result);
                } else {
                    result = currentValue;
                }
            }
        }
        return result;
    }
}

Why does storing the values in ConcurrentHashMap make the code threadSafe? I think that it is still possible that the ComplexObject won't be completely initialized and this "partial object" will be saved in the map. And other threads will be reading partial not fully initialized objects.

I think I know what is "happens-before", I've analyzed code in JDK 8.0_31 and I still don't know the answer.

I am aware of the functions like computeIfAbsent, putIfAbsent. I know that this code can be written differently. I just wan't know details which make this code threadsafe.


Solution

  • Happens before actually is the key here. There's a happens before edge extending from map.put(key, object) to a subsequent map.get(key), therefore the object you retrieve is at least as up to date as it was at the time it was stored in the map.