Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/373.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用双重检查习惯用法纠正错误 公共类生成器{ 私有静态最终映射缓存=新HashMap(); 公共静态字节[]生成(字节[]src){ byte[]generated=cache.get(src); if(生成==null){ 已同步(缓存){ generated=cache.get(src); if(生成==null){ 生成=数据生成(src); cache.put(src,已生成); } } } 产生的回报; } 私有静态字节[]数据生成(字节[]src){…}_Java_Multithreading - Fatal编程技术网

Java 使用双重检查习惯用法纠正错误 公共类生成器{ 私有静态最终映射缓存=新HashMap(); 公共静态字节[]生成(字节[]src){ byte[]generated=cache.get(src); if(生成==null){ 已同步(缓存){ generated=cache.get(src); if(生成==null){ 生成=数据生成(src); cache.put(src,已生成); } } } 产生的回报; } 私有静态字节[]数据生成(字节[]src){…}

Java 使用双重检查习惯用法纠正错误 公共类生成器{ 私有静态最终映射缓存=新HashMap(); 公共静态字节[]生成(字节[]src){ byte[]generated=cache.get(src); if(生成==null){ 已同步(缓存){ generated=cache.get(src); if(生成==null){ 生成=数据生成(src); cache.put(src,已生成); } } } 产生的回报; } 私有静态字节[]数据生成(字节[]src){…},java,multithreading,Java,Multithreading,} 有人能回答吗,这个密码出了什么问题? 也许generate()方法可以返回部分构造的数组,不是吗?首先,您使用的是未同步的哈希映射。比如说,这可能导致无限循环(这是真实的——在生产环境中已经观察到) 它还有一个常见的双重检查锁定错误,即从写入数组内容到读取数组(我假设会有一些数据)之间没有“发生在”关系 (注意,此处比较的是对象标识,而不是数组内容。) 另一个问题是,您有可变静态,这是一种糟糕的设计,会导致严重的问题。当一个线程正在执行第11行时,另一个线程可能正在执行第5行。这就是为什么在

}

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

首先,您使用的是未同步的
哈希映射。比如说,这可能导致无限循环(这是真实的——在生产环境中已经观察到)

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

(注意,此处比较的是对象标识,而不是数组内容。)


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

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


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

用ConcurrentHashMap替换HashMap

您的同步看起来没有用。我会这样改写整个故事:

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) {...}
private静态最终映射缓存=新的ConcurrentHashMap();
公共静态字节[]生成(字节[]src,int计数器){
byte[]generated=cache.get(src);
if(生成==null){
生成=数据生成(src);
cache.put(src,已生成);
}        
产生的回报;
}

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

但在HashMap中,我只使用get()方法,而不使用synchronize。我可以从HashMap中获取不正确的数据吗?(put()放在同步块中,任何更改都必须在同步块完成后在主内存中获取)@artem,如果put()在get()使用这些结构时更改了映射的内部结构,则get()可能会像Tom所描述的那样失败。(所有地图的使用都必须同步。)你能告诉我更多,会出现什么问题吗。我不明白。我认为在最坏的情况下HashMap返回null…我不认为这个例子有“常规”的双重检查错误,因为
synchronize
建立了一个before-before关系(根据这个:)@Arian-快速情况不会进入
synchronized
块。因此,以前没有发生过。你能详细告诉我会出什么问题吗。我认为在最坏的情况下,generate()可以返回null。。。或者它可以返回部分构造的数组或日期不一致的数组,不是吗?副作用是特定于实现的-契约在这里很重要,该契约要求在多个线程访问HashMap,并且至少一个线程更改内容的情况下,访问必须同步。ConcurrentModificationException将是完全有效的结果。API文档中写道:“如果多个线程同时访问一个哈希映射,并且至少有一个线程在结构上修改了该映射,那么它必须在外部进行同步。”在该特定代码中,主要关注的不是生成值的潜在开销。最终可能会为同一个键获得不同的值。(
putIfAbsent
在这里很有用。当然不要使用断开的双重检查锁定。)原始答案/代码具有误导性。ConcurrentHashMap必须与putIfAbsent()一起使用,如@TomHawtin tackline所述
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;
}