单一检查惯用法可用于实现线程安全延迟初始化(与双重检查相比)可能的缺点,即多个并发内容浪费一些计算时间。它是
单一检查成语
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) {
field = result = computeFieldValue();
}
return result;
}
在这里,我们需要volatile field
来避免将部分初始化的对象传递给另一个线程,即 - 赋值(write)隐式执行必要的同步。
我想实现一个参数化延迟初始化缓存,它基本上由Map<Integer, Object>
表示,其中每个元素都是使用惰性初始化创建的。
我的问题是:是否足以使用ConcurrentHashMap
来避免部分初始化的问题。也就是说,在这种情况下,使用单一检查习语的lazy-init缓存的线程安全实现可以由
private final ConcurrentHashMap<Integer, ItemType> items = new ConcurrentHashMap<Integer, ItemType>();
ItemType getItem(Integer index) {
ItemType result = items.get(index);
if (result == null) {
result = computeItemValue(index);
items.put(index, result);
}
return result;
}
换句话说:我假设&#39; items.put(索引,结果)&#39;执行必要的同步(因为它是写入)。请注意,问题可能是双重的:首先我想知道这是否适用于(a)当前的JVM实现,第二(更重要的是)我想知道ConcurrentHashMap
是否保证(给定文档/合同)。 / p>
注意:这里我假设computeItemValue生成一个不可变对象,并在单一检查习语的意义上保证线程安全(也就是说,一旦完成对象构造,返回的对象对所有线程的行为都相同)。我认为这是J. Bloch的书中的第71项。
答案 0 :(得分:3)
在java 8中,您可以使用computeIfAbsent来避免重复初始化的可能性:
private final ConcurrentHashMap<Integer, ItemType> items = new ConcurrentHashMap<Integer, ItemType>();
ItemType getItem(Integer index) {
return items.computeIfAbsent(index, this::computeItemValue);
}
答案 1 :(得分:1)
是否足以使用ConcurrentHashMap来避免问题 部分初始化。
是的,在从CHM读取的对象相对于被写入的顺序之前有一个明确的发生。
所以,你覆盖了能见度,但还没有触及原子性。原子性有两个组成部分
你也没有实现。也就是说,对于memoization,您可以创建多个对象(实际上不是lazy-init)。对于put,如果不存在,CHM可以接收多个值,因为您没有调用putIfAbsent
。
根据您的上次更新,如果它们将是同一个对象并且是不可变的,那么尽管有额外的构造,您应该没问题。