【问题标题】:Which concurrent collection to use?使用哪个并发集合?
【发布时间】:2023-03-20 02:55:02
【问题描述】:

我有一个场景,其中未知数量的线程将元素添加到服务器上的集合中。此集合中的数据不必排序,也不会被迭代。这个集合只需要两个简单的操作:

  1. 添加元素(在某些情况下删除旧元素)
  2. 从集合中读取所有元素(不是一个一个,而是整个集合,以便对其进行序列化并将其发送给客户端。当然,这些元素也可以移动到另一个集合,然后再进行序列化。)

哪个集合最适合这个用例?我会选择ConcurrentHashMap,不知道这个选择好不好。

编辑:我忘记了一个重要的要求:如果某个类型的元素已经在这个集合中并且添加了另一个相同类型的元素,那么在添加新元素之前应该删除旧元素。对于这个要求,我想使用哈希值来避免搜索。存储的对象很简单:它们包含一个唯一的用户名和一些字符串和整数。对象的用户名应该用作键。

【问题讨论】:

  • 为什么选择 ConcurrentHashMap?您在查找键值对吗?
  • 如果这是您真正需要做的,那么CopyOnWriteArrayList 就可以完成这项工作;但是,请注意每次迭代都会创建一个新副本。因此,如果您期望有很多读者,这可能不是理想的选择...
  • 根据文档,从 CopyOnWriteArrayList 添加和删除元素非常昂贵。
  • 如果您使用用户名作为键,对象作为值,则似乎指示了 ConcurrentHashMap。

标签: java collections concurrency


【解决方案1】:

我认为最好使用的实际上是 ConcurrentSkipListSet 。原因:

迭代器是弱一致的,返回的元素反映了 集合在创建时或之后的某个时间点的状态 迭代器。它们不会抛出 ConcurrentModificationException,并且可能 与其他操作同时进行。升序视图 并且它们的迭代器比降序更快。

这意味着您可以浏览整个列表并阅读所有项目,同时添加其他项目。它是完全并发的!

请注意,添加项目需要 O(logN) 时间。

【讨论】:

  • 谢谢。是的,当读取 ConcurrentHashMap 中的数据时,它可能已经过时了,但是读取哈希值比遍历树更快。
  • 但其中一项要求是您可以同时阅读所有项目。 ConcurrentHashMap 的迭代器并非设计用于多线程:“迭代器设计为一次只能由一个线程使用。” docs.oracle.com/javase/6/docs/api/java/util/concurrent/…
  • 对不起,因为在这方面我一直不精确:我不想读取每个元素,而是一次读取整个数据,然后将其发送到远程主机。客户端然后从流中读取对象并将数据存储在 TreeSet 中以按特定顺序显示。
  • 如果你检查每个项目,并将它们放入一个新的集合中,它是一样的。如果您使用 ConcurrentHashMap ,则不能使用多个线程,因此您将失去此操作的并发性。还有另一个并发集合,它以原子方式收集所有项目,但是写入集合的速度要慢得多,所以我不推荐它。
  • @androiddeveloper:Javadoc 谈到 CHM 迭代器 instances 并不意味着可以在多个线程中使用,当其他线程时,您仍然可以从某个线程修改 CHM 实例正在迭代 CHM 的值(具有与 CSLS 迭代器 afaik 相同的弱一致语义)。
【解决方案2】:

是的,ConcurrentHashMap 适合这个。在映射中使用用户名作为键类型(K)和关联的用户信息(“一些字符串和整数”)作为值类型(V)。使用put 添加新的键值对,使用remove 删除键值对,使用entrySet 获取容器中的所有键值对(如果这就是您所说的“从集合”)。

【讨论】:

  • 是的,我已经做到了:值是对象本身,键是对象属性。我用synchronized(map) {.....} 包围了像put()remove() 这样的方法调用。是否有任何规则应该如何正确配置 ConcurrentHashMap?默认的 concurrencyLevel 为 16 可能通常是多余的。
  • @user1812379:您不需要使用synchronized(map) { ... } 来使用putremove 或对CHM 实例的任何其他操作——这会破坏使用CHM 类的目的。 CHM 上的操作已经是原子的,请参阅文档以获取更多详细信息。至于concurrencyLevel 的最合适的值,Javadoc 有一些注释,但如果它真的很重要,您应该自己测量(在实际工作负载下),因为“最佳”值因情况而异(这就是允许您配置它的原因)。
【解决方案3】:

它不是标准库的一部分,但您可以使用this concurrent doubly linked list。它的迭代器是弱一致的,不会抛出ConcurrentModificationException,也可以使用toArray,循环遍历返回的数组。

【讨论】:

    【解决方案4】:

    我相信java.util.concurrent. CopyOnWriteArrayList 中有一个并发列表实现,可以满足您的要求。

    或者你可以使用:

     List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());
    

    【讨论】:

    • 谢谢,但这不太适合像添加元素这样的许多可变操作。
    猜你喜欢
    • 2013-01-23
    • 2010-10-11
    • 2012-10-21
    • 1970-01-01
    • 2011-05-01
    • 1970-01-01
    • 2019-02-11
    • 1970-01-01
    • 2011-05-16
    相关资源
    最近更新 更多