本文介绍了ConcurrentHashMap:使用“putIfAbsent”避免额外的对象创建?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在多线程环境中聚合键的多个值。钥匙事先不知道。我想我会做这样的事情:

I am aggregating multiple values for keys in a multi-threaded environment. The keys are not known in advance. I thought I would do something like this:

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 的新实例,然后我扔掉(在大多数情况下)。这似乎是无理滥用垃圾收集器。是否有一种更好的,线程安全的方法来初始化这种结构而无需同步 记录方法?决定让 putIfAbsent 方法不返回新创建的元素,并且缺少一种延迟实例化的方法,除非它被调用,我感到有些惊讶(所以说话。

The problem I see is that every time this method runs, I need to create a new instance of an ArrayList, which I then throw away (in most cases). This seems like unjustified abuse of the garbage collector. Is there a better, thread-safe way of initializing this kind of a structure without having to synchronize the record method? I am somewhat surprised by the decision to have the putIfAbsent method not return the newly-created element, and by the lack of a way to defer instantiation unless it is called for (so to speak).

推荐答案

Java 8引入了一个API来满足这个确切的问题,制作了一个解决方案:

Java 8 introduced an API to cater for this exact problem, making a 1-line solution:

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))之后, get(key)保证一致在线程之间,因此上面的代码是线程安全的。

The special method putIfAbsent(K, V)) will either put your value object in, or if another thread got before you, then it will ignore your value object. Either way, after the call to putIfAbsent(K, V)), get(key) is guaranteed to be consistent between threads and therefore the above code is threadsafe.

唯一浪费的开销是,如果某个其他线程同时为同一个键添加一个新条目:你可能最终丢弃新创建的值,但只有在没有条目的情况下才会发生您的线程丢失的竞赛,这通常很少见。

The only wasted overhead is if some other thread adds a new entry at the same time for the same key: You may end up throwing away the newly created value, but that only happens if there is not already an entry and there's a race that your thread loses, which would typically be rare.

这篇关于ConcurrentHashMap:使用“putIfAbsent”避免额外的对象创建?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 09:18