为什么ConcurrentHashMap在Double Checked Locking中工作

时间:2015-03-09 21:04:19

标签: java multithreading double-checked-locking

在书中" Java Concurrency in Practice"提到以下代码不是线程安全的:

@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;
    }
}

它不是线程安全的,因为: - 一个线程可以创建新的Resource实例 - "中的另一个线程;如果" condition可以得到非空引用但是Resource的对象不会被完全初始化

在此question中是类似的代码。资源存储在concurentHashMap中,人们说它是threadSafe。像这样:

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;
    }
}

为什么在ConcurrentHashMap中存储值会使代码threadSafe ?我认为仍然有可能ComplexObject不会被完全初始化并且这个&#34;部分对象&#34;将保存在地图中。其他线程将读取部分未完全初始化的对象。

我想我知道什么是&#34;发生 - 之前&#34;,我已经分析了JDK 8.0_31中的代码,我仍然不知道答案。

我知道 computeIfAbsent putIfAbsent 等功能。我知道这段代码可以用不同的方式编写。我只是不知道使这些代码线程安全的细节。

1 个答案:

答案 0 :(得分:4)

发生之前实际上是关键点。在边缘从map.put(key, object)延伸到后续map.get(key)之前发生了,因此您检索的对象至少与其当时的最新版本相同存储在地图中。