双重检查锁定是否可以与Java中的最终Map一起使用?

sti*_*kj1 5 java multithreading

我正在尝试实现线程安全的Map缓存,并且希望对缓存Strings进行延迟初始化。这是我实现的第一步:

public class ExampleClass {

    private static final Map<String, String> CACHED_STRINGS = new HashMap<String, String>();

    public String getText(String key) {

        String string = CACHED_STRINGS.get(key);

        if (string == null) {

            synchronized (CACHED_STRINGS) {

                string = CACHED_STRINGS.get(key);

                if (string == null) {
                    string = createString();
                    CACHED_STRINGS.put(key, string);
                }
            }
        }

        return string;
    }
}
Run Code Online (Sandbox Code Playgroud)

编写完这段代码后,Netbeans就“双重检查锁定”警告了我,因此我开始对其进行研究。我找到了“双重检查锁定已损坏”声明并已阅读,但我不确定我的实现是否会受到它提到的问题的影响。看来本文中提到的所有问题都与newsynchronized块中使用运算符进行对象实例化有关。我没有使用new运算符,并且字符串是不可变的,因此我不确定本文是否与这种情况相关。这是在字符串中缓存字符串的线程安全方法HashMap吗?线程安全性是否取决于createString()方法中采取的措施?

wes*_*ton 5

不,这是不正确的,因为第一次访问是在同步块之外完成的。

这在某种程度上取决于如何实施getput可能实施。您必须牢记它们不是原子操作

例如,如果它们是这样实现的:

public T get(string key){
    Entry e = findEntry(key);
    return e.value;
}

public void put(string key, string value){
    Entry e = addNewEntry(key);
    //danger for get while in-between these lines
    e.value = value;
}

private Entry addNewEntry(key){
   Entry entry = new Entry(key, ""); //a new entry starts with empty string not null!
   addToBuckets(entry); //now it's findable by get
   return entry; 
}
Run Code Online (Sandbox Code Playgroud)

现在当操作仍在进行中时get可能不会返回,并且整个方法可能返回错误的值。nullputgetText

该示例有点复杂,但您可以看到代码的正确行为依赖于地图类的内部工作。这不好。

虽然您可以查看该代码,但您无法考虑编译器、JIT 和处理器优化以及内联,这些优化和内联可以有效地改变操作顺序,就像我选择编写该映射实现的古怪但正确的方式一样。