java - 使用 ConcurrentHashMap 实现参数化线程安全的延迟初始化缓存(不带 volatile 关键字)

标签 java multithreading concurrency lazy-initialization

单次检查惯用法可用于实现线程安全的惰性初始化,但(与双重检查相比)可能存在多个并发初始化浪费一些计算时间的缺点。这是

单选习语

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/

相关文章:

java - 如何使用同一个套接字发送二进制文件和文本

c# - Task.WaitAll() 没有按预期工作

不影响性能的Java线程

c# - 如何改进这个可重用的方法类?

android - AsyncTask 在线程完成之前不调用 onPreExecute

java - 阻止给定对象上的所有线程

java - 将视频从 IP 摄像机流式传输到 JavaFX 应用程序

java - 使用 Maven 创建控制台应用程序时最好的原型(prototype)是什么?

java - 如何不断要求用户输入直到输入为空?

multithreading - 在 sbcl lisp 中使用线程时,变量在 lisp 中未绑定(bind)