【问题标题】:One concurrent collection inside another: is it thread safe一个并发集合在另一个集合中:它是线程安全的吗
【发布时间】:2014-03-29 08:40:33
【问题描述】:

[问题]:使用ConcurrentHashMap<Object, ConcurrentHashMap<Object, Object>> 是否线程安全。

[可选回答]:还有其他并发地图类型呢?那么并发集合呢?

P.S. 我只问java.util.concurrent 包。

具体使用上下文:

//we have
ConcurrentHashMap<Object, ConcurrentHashMap<Object, Object>> map = new ConcurrentHashMap<Object, ConcurrentHashMap<Object, Object>>();
//each string can be executed separately and concurently
ConcurrentHashMap<Object, Object> subMap = new ConcurrentHashMap<Object, Object>()
map.put(key, subMap);
map.remove(key);
map.get(key);
map.get(key).put(key, ref);
map.get(key).remove(key);

也许我的解决方案是围绕 Guava HashBasedTable?

【问题讨论】:

  • 这是为什么呢? ConcurrentHashMap 毕竟是线程安全的。
  • @Keppil 我希望有人描述不是这样的场景
  • 该代码似乎无法编译。没有 map.put(object) 方法。
  • @BrettOkken 我已经更新了

标签: java collections map concurrency


【解决方案1】:

如果没有您计划使用集合的特定上下文,您就无法定义线程安全。

您命名的并发集合本身是线程安全的,因为它们的内部不变量不会被并发访问破坏;但这只是线程安全检查表上的一个要点。

如果您在结构上执行的操作不止一个操作,而且整个结构必须是原子的,那么仅使用这些类就不会获得线程安全。您将不得不求助于经典的锁定,或者一些非常复杂且通常没有动机的无锁更新方案。

使用您问题中的示例,考虑以下内容。

线程 1 执行

map.get(mapKey).put(key, value);

同时线程2执行

map.remove(mapKey);

结果如何?线程 1 可能正在将一些东西放到已经被删除的地图上,或者它甚至可能从 get 获得 null 结果。在大多数情况下,正确性需要更多的协调。

【讨论】:

  • 似乎是我必须理解的东西,但我不明白。请问,你能提供一个简单的例子和​​解释吗?
  • 查看我的问题的更新版本。我添加了上下文定义。
  • asMap 是什么?这不是ConcurrentHashMap的方法。
  • 对不起。我把它和另一个关于番石榴地图的问题混淆了。我已经将add 方法更改为put。不应该编译
  • map.put(subMap) 肯定不会编译。
【解决方案2】:

一般来说,作为 java.util.concurrent 一部分的类提供了额外的性能,但会增加编码复杂度的(潜在)代价。

我看到的嵌套 ConcurrentMap 实例的问题是管理使用给定键的值填充外部映射。如果预先知道所有键并且在某种初始化阶段将值放置在映射中,则没有问题(但您也可能不需要将外部映射设为 ConcurrentMap)。如果您需要能够随时将新地图插入到外部地图中,则工作会变得更加复杂。在创建要插入外部地图的新地图时,您需要使用 putIfAbsentmethod[1] 并注意返回值以确定要向哪个实例添加数据。

[1] - http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentMap.html#putIfAbsent(K,%20V)

【讨论】:

  • 你想说如果我使用puIfAbsent 方法将是我的场景线程安全的吗?
  • 几乎没有足够的数据可以肯定地说可以使整个场景线程安全。使用该方法填充外部地图几乎肯定是所需工作的一部分。必须知道的是,哪些工作必须被视为原子的。
  • remove 的使用也带来了挑战。在删除期间,或多或少不可能判断竞争线程是否实际上在您要删除的键处操作映射。一旦设置了值,只在值中保留一个空映射会简单得多。
【解决方案3】:

ConcurrentHashMap 的 javadoc 是这样说的:

但是,即使所有操作都是线程安全的,检索操作也不需要锁定,并且不支持以阻止所有访问的方式锁定整个表

因此,就修改而言,它们是线程安全的。

更新

同样的 javadoc http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html 说:

检索操作(包括 get)一般不会阻塞,因此可能与更新操作(包括 put 和 remove)重叠。检索反映了最近完成的更新操作在其开始时保持的结果。对于 putAll 和 clear 等聚合操作,并发检索可能仅反映插入或删除某些条目。类似地,迭代器和枚举返回反映哈希表在创建迭代器/枚举时或之后的某个时间点的状态的元素。 它们不会抛出 ConcurrentModificationException。但是,迭代器被设计为一次只能由一个线程使用。

【讨论】:

  • 请在它们不是线程安全的情况下使用。请。那么HashTable呢?
  • 我想说 Hashtable 以及几乎所有 java 基本集合类型在阅读上下文中都是线程安全的。只有同时写入和/或读取时,才会遇到ConcurrentModificationException 之类的问题。
  • 是否有任何 java 映射在并发读写上下文中是安全的(就像在我定义的上下文部分中一样)?
【解决方案4】:

并发集合意味着多个线程可以同时对集合执行添加/删除操作,不,它不是线程安全的

更多详情:

更多内容请阅读

What's the difference between ConcurrentHashMap and Collections.synchronizedMap(Map)? Is ConcurrentHashMap totally safe?

【讨论】:

  • 为什么 ConcurrentHashMap 不安全?
  • @VolodymyrBakhmatiuk 请再读一遍我已经更新了我的答案
【解决方案5】:

并发集合对于读取是线程安全的;但是您必须期待ConcurrentModificationException,以防发生竞争并发更新或在另一个线程对其进行迭代时修改集合。

【讨论】:

  • 见injecteer的回答。他说它们对于写入是线程安全的。谁是对的?请多解释
猜你喜欢
  • 1970-01-01
  • 2021-07-10
  • 2013-01-26
  • 2011-05-23
  • 1970-01-01
  • 2018-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多