当前位置: 首页 > 面试题库 >

ConcurrentHashMap:避免使用“ putIfAbsent”创建额外的对象?

倪炎彬
2023-03-14
问题内容

我正在多线程环境中聚合键的多个值。密钥未知。我以为我会做这样的事情:

class Aggregator {
    protected ConcurrentHashMap<String, List<String>> entries =
                            new ConcurrentHashMap<String, List<String>>();
    public Aggregator() {}

    public void record(String key, String value) {
        List<String> newList =
                    Collections.synchronizedList(new ArrayList<String>());
        List<String> existingList = entries.putIfAbsent(key, newList);
        List<String> values = existingList == null ? newList : existingList;
        values.add(value);
    }
}

我看到的问题是,每次运行此方法时,我都需要创建一个新的实例,ArrayList然后将其丢弃(在大多数情况下)。这似乎是对垃圾收集器的不合理滥用。是否有更好的,线程安全的方法来初始化这种结构而不必使用synchronizerecord方法?我对使该putIfAbsent方法不返回新创建的元素的决定感到惊讶,并且对缺少除非被要求(可以这么说)实例化实例的延迟的方法感到惊讶。


问题答案:

Java 8引入了一个API来解决这个确切的问题,提出了一种1行解决方案:

public void record(String key, String value) {
    entries.computeIfAbsent(key, k -> Collections.synchronizedList(new ArrayList<String>())).add(value);
}

对于Java 7:

public void record(String key, String value) {
    List<String> values = entries.get(key);
    if (values == null) {
        entries.putIfAbsent(key, Collections.synchronizedList(new ArrayList<String>()));
        // At this point, there will definitely be a list for the key.
        // We don't know or care which thread's new object is in there, so:
        values = entries.get(key);
    }
    values.add(value);
}

这是填充时的标准代码模式ConcurrentHashMap

特殊方法putIfAbsent(K, V))将放入您的值对象,或者如果在您之前有另一个线程,那么它将忽略您的值对象。无论哪种方式,都确保在调用之后putIfAbsent(K, V))get(key)线程之间保持一致,因此上述代码是线程安全的。

唯一浪费的开销是,如果其他某个线程在同一时间为同一键同时添加新条目:您 可能 最终 丢弃新创建的值,但这只会在尚无条目
存在种族冲突的情况下发生线程丢失,这种情况通常很少见。



 类似资料:
  • 问题内容: Java Docs表示,相当于 因此,如果键存在于地图中,则不会更新其值。这样对吗? 如果我想根据某些条件更新键值怎么办?说出到期时间等 添加和更新缓存会更好吗? 问题答案: 因此,它不会更新键的值。这样对吗? 那是正确的。它将返回地图中已经存在的当前值。 这对于添加和更新缓存会更好吗? 有几件事可以使您的实现更好。 1. 您不应该使用putIfAbsent来测试它是否存在,只有在要确

  • 此外,我不想将开发人员从ProjectManager转移到JavaProjectManager,因为我有更多从ProjectManager扩展的类,需要开发人员。

  • 问题内容: 我有一个实现MVP模式的应用程序,该应用程序具有来维护主持人对象在视图娱乐上的作用(这里有关于此的文章)。我是Dagger 2的新手,它试图与当前代码一起实现。 我设法使它起作用,但是现在我的演示者被创建了两次。首先,它是使用在中初始化的工厂类创建的,但是随后,在添加Dagger 2实现时,我创建了两个对象(在工厂类中和注入时)。 现在,我避免在中创建新的演示者,而改为通过注入的演示者

  • 今天我开始学习ReactJS,一个小时后我就开始面对这个问题。。我想在页面上的div中插入一个有两行的组件。下面是我正在做的一个简化示例。 我有一个html: 渲染功能如下: 下面我调用渲染: 生成的HTML如下所示: 我不是一个非常高兴的问题,这个问题迫使我将所有内容都打包在一个div“DeadSimpleComponent”中。在没有显式DOM操作的情况下,最好且简单的解决方法是什么? 201

  • 垃圾回收期在管理内存方面非常出色,它非常高效地移除不再使用的对象。但是无论你如何看待它,分配和销毁一个基于堆内存的对象花费处理器时间比分配和销毁不是基于堆内存的对象要多。在函数内创建大量的引用类型对象会引入严重的性能消耗问题。 所以不能让垃圾回收器超负荷工作。你可以借鉴一些简单的技巧最小化垃圾回收器的工作。所有的引用类型对象,即使是局部变量,都被分配存储在堆内存上。每个引用类型的局部变量在函数结束

  • 目前,我正在尝试使用Java 9和javax开发一些代码。编辑xml文件的xml库(对于我的任务都是必需的),我在添加子节点时遇到了一些奇怪的问题。 这是XML文件: 我想编辑它构建如下内容: 现在,代码的第一次运行在<代码> 这是运行程序2次后生成的XML。如您所见,它在<代码> 在更新文件之前想知道这些节点的内容是什么,我写了下一段代码: 第一次执行: 第二次执行: 第三次执行 最后,这是Ja