【问题标题】:ConcurrentHashMap - adding items with the same KeyConcurrentHashMap - 添加具有相同键的项目
【发布时间】:2019-04-28 01:28:17
【问题描述】:

我有一个 CuncurrentHasMap:

private static ConcurrentHashMap<String, Object> testParameters =
new ConcurrentHashMap<String, Object>();

我正在推送(有时)具有相同键的对象,这些对象是 TreeMap(string, MyPrivateClass) 类型的 TreeMap。

如何改变这个put方法,让相同key的对象放在一起不被覆盖?

public static void put(String key, Object object) {

        getTestParameters().put(key, object);
}

【问题讨论】:

  • will be placed together ,这是什么意思?为此,您需要使用&lt;String,Collection&lt;Object&gt;&gt; 的地图。
  • 也许你想使用另一种数据结构。 ConcurrentHashMap 的每个键只有一个值。
  • 你想要某种值的合并吗?
  • placed together 是什么意思?您想将新的 treeMap 与当前的 treeMap 合并,还是想同时引用这两个 treeMap?在后一种情况下,您应该声明您的测试参数,如列表映射:ConcurrentHashMap&lt;String, List&lt;Object&gt;&gt; testParameters,以防键已经存在。
  • @Selaron : 是的,我想将新的 treeMap 与当前的树图合并!

标签: java concurrency


【解决方案1】:

将值设为List&lt;Object&gt;(或其他一些容器/集合类型),而不是Object

private static ConcurrentHashMap<String, List<Object>> testParameters =
    new ConcurrentHashMap<>();

然后使用compute 而不是put 创建并添加到列表中:

getTestParameters().compute(key, (k, v) -> {
  if (v == null) v = new ArrayList<>();
  v.add(object);
  return v;
});

【讨论】:

  • @Oleksandr 这不是原子的,所以不完全相同。您必须将 Collections.synchronizedList(new ArrayList&lt;&gt;()) 添加到地图中才能安全工作。
  • @Oleksandr computeIfAbsent 是原子的; computeIfAbsentadd 不是。独占执行computeIfAbsent后,两个线程可以尝试同时执行add
  • 我在收到@user167019 的通知后更正了我的答案
【解决方案2】:

如果您可以接受,您可以使用Guava Multimap 的实现。它可以为单个键存储多个值。因为看起来您需要一个线程安全的 Map,所以我会使用 Multimaps.synchronizedMultimap method 并执行以下操作:

Multimap<String, Object> multimap = HashMultimap.create();
Multimap synchronizedMultimap = Multimaps.synchronizedMultimap(multimap);

【讨论】:

  • 如何将这个类导入我的项目?
【解决方案3】:

正如您在 cmets 中所述,新的 TreeMap 类型值应该与现有值 合并,您可以像这样重写您的字段和 put 方法:

private static final Map<String, Map<String, MyPrivateClass>> testParameters = new ConcurrentHashMap<>();

public static void put(String key, TreeMap<String, MyPrivateClass> value) {
    testParameters.compute(key, (k,v) -> {
            if(null == v) {
                v = Collections.synchronizedMap(new TreeMap<>(value.comparator()));
            }
            v.putAll(value);
            return v;
        }
    );
}

使用 ConcurrentHashMap 表明您将要从多个线程同时访问映射,这就是我添加 Collections.synchronizedMap() 的原因,因为您可能还希望在生成的 TreeMap 上同时执行读取。

更新:在保持 TreeMaps 条目的同时,我从 computeIfAbsent 切换到 compute,正如 AndyTurner 在他的回答中所做的那样,因为我以前的方法不是原子的。

【讨论】:

  • 第一个解决方案是错误的,不能解决 OP 指出的竞争条件。 get() 后跟 put() 不是原子的,因此可能导致 OP 试图避免的确切问题。第二种解决方案同样缺乏原子性,因此也是不正确的。 @andy-turner 的解决方案是正确的。
  • @user167019 感谢您的反馈,我完全放弃了第一个解决方案,并将第二个解决方案变成了原子的。在执行 computeIfAbscent 之后,我的第二个解决方案中仍然存在竞争条件,因此不知道两个或多个线程中的哪一个将是第一个添加其映射条目的线程。 Andys 的解决方案是错误的,因为它只会将越来越多的 TreeMaps 添加到列表中,而不是按照操作员的要求合并/更新它们的条目。此外,由于 ArrayList 已知不是线程安全的,因此在并发写入期间,以后对该列表的读取可能会失败。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-15
  • 2013-04-04
  • 2013-02-16
  • 2022-09-29
  • 2011-06-18
相关资源
最近更新 更多