正确使用复核成语

时间:2010-11-11 11:19:48

标签: java multithreading

public class Generator {
private static final Map<byte[], byte[]> cache = new HashMap<byte[], byte[]>();

public static byte[] generate(byte[] src) {
    byte[] generated = cache.get(src);
    if (generated == null) {
        synchronized (cache) {
            generated = cache.get(src);
            if (generated == null) {
                generated = doGenerate(src);
                cache.put(src, generated);
            }
        }
    }
    return generated;
}

private static byte[] doGenerate(byte[] src) {...}

}

有人可以回答,这段代码有什么问题吗? 也许generate()方法可以返回部分构造的数组,不是吗?

3 个答案:

答案 0 :(得分:3)

用ConcurrentHashMap替换你的HashMap。

您的同步看起来没用。我会用这种方式重写整个事情:

private static final Map<byte[], byte[]> cache = new ConcurrentHashMap<byte[], byte[]>();

public static byte[] generate(byte[] src, int counter) {
    byte[] generated = cache.get(src);
    if (generated == null) {
        generated = doGenerate(src);
        cache.put(src, generated);
    }        
    return generated;
}

编辑:try / catch是奇怪的,没有必要。出于OP的目的,使用ConcurrentHashMap就足够了。如果OP真的不想承担为同一数据集调用doGenerate两次的成本,那么他们将不得不继续使用DCL模式,但仍然建议使用ConcurrentHashMap。

答案 1 :(得分:2)

首先,您使用的是HashMap非同步。这可能导致无限循环(这是真实的 - 它已在生产环境中观察到)。

它还遇到了一般的双重检查锁定错误,即没有发生在之间的关系,从写入数组的内容到读取它(我假设会有一些数据)

(注意,您在这里比较对象标识,而不是数组内容。)

另一个问题是你有可变的静态,这是一个糟糕的设计,会导致严重的问题。

答案 2 :(得分:0)

当一个线程正在执行第11行时,另一个线程可能正在执行第5行。这就是为什么你需要一个同步的地图,以及为什么当使用非同步的HashMap时你的结果将是不确定的。

此处的解决方案是同步Map(请参阅java.util.Collections#synchronizedMap)。当然,如果使用完全同步的映射,则双重检查锁定(额外优化)将变得多余。