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

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

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);
    }
}

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


答案 1

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)

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


答案 2

从 Java-8 开始,您可以使用以下模式创建多映射:

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

ConcurrentHashMap 文档(不是通用协定)指定,每个键只创建一次 ArrayList,但代价是在为新键创建 ArrayList 时延迟更新:

http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#computeIfAbsent-K-java.util.function.Function-


推荐