【发布时间】:2012-05-31 09:34:40
【问题描述】:
我正在为多线程环境中的键聚合多个值。密钥是事先不知道的。我以为我会做这样的事情:
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 方法不返回新创建的元素的决定感到有些惊讶,而且除非需要(可以这么说),否则缺乏延迟实例化的方法。
【问题讨论】:
-
除非有基准,否则不要担心额外的对象。短期对象分配/GC 在现代 JVM 中很便宜。 (我猜“some”不仅仅是“none”,但现代 JVM 通常对此没有任何问题。)无论如何,这仍然是一个有趣的问题,因为它有望产生一些有趣的方法。 “延迟”分配等在 Java 中有点尴尬,因此并不常见,因为缺少闭包或“按名称传递”语义。 (匿名类并不那么性感,通常需要有一个相应的接口以及
putIf..的重载)。 -
两年多过去了,仍然是一个很好的问题。我真的希望 Java 1.8 为默认对象的延迟实例化添加了类似
putIfAbsent (K key, Supplier<V> value)的内容。它肯定已经在ConcurrentMap接口上改进了对流 API 的其他支持。 -
@sparc_spread Java 1.8 向
Map和ConcurrentMap添加了一个computeIfAbsent(K key, Function<K, V> mappingFunction)方法,该方法应该可以满足您对默认对象的延迟实例化的要求。
标签: java synchronization thread-safety concurrenthashmap