单次检查惯用法可用于实现线程安全的惰性初始化,但(与双重检查相比)可能存在多个并发初始化浪费一些计算时间的缺点。这是
单选习语
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) {
field = result = computeFieldValue();
}
return result;
}
在这里,我们需要 volatile field
以避免将部分初始化的对象传递给另一个线程,即赋值(写入)隐式执行必要的同步。
我想实现一个参数化延迟初始化缓存,它本质上由 Map<Integer, Object>
表示,其中每个元素都是使用惰性初始化创建的。
我的问题是:使用ConcurrentHashMap
就足够了吗?以避免部分初始化的问题。也就是说,在这种情况下,使用单检查惯用法的惰性初始化缓存的线程安全实现可以由以下给出:
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;
}
换句话说:我假设“items.put(index, result)”执行必要的同步(因为它是写入)。请注意,这里的问题可能是双重的:首先我想知道这是否适用于(a)当前的 JVM 实现,其次(更重要的是)我想知道这是否得到 ConcurrentHashMap
的保证(给定文档/契约(Contract))。 .
注意:这里我假设computeItemValue生成一个不可变的对象,并保证单次检查惯用法意义上的线程安全(也就是说,一旦对象构造完成,返回的对象对于所有线程都具有相同的行为)。我认为这是 J. Bloch 书中的第 71 项。
最佳答案
在java 8中,您可以使用computeIfAbsent甚至避免重复初始化的可能性:
private final ConcurrentHashMap<Integer, ItemType> items = new ConcurrentHashMap<Integer, ItemType>();
ItemType getItem(Integer index) {
return items.computeIfAbsent(index, this::computeItemValue);
}
关于java - 使用 ConcurrentHashMap 实现参数化线程安全的延迟初始化缓存(不带 volatile 关键字),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31944568/