【问题标题】:Java Concurrent collection for few writes and frequent readsJava 并发集合,少写多读
【发布时间】:2016-12-15 22:06:57
【问题描述】:

我想使用基于比较器的键值映射。这将具有读取和罕见的写入操作(每 3 个月通过调度程序执行一次)。集合的初始加载将在应用程序启动时完成。 另请注意,写入将:

  • 向地图添加单个条目
  • 不会修改地图的任何现有条目。

ConcurrentSkipListMap 将是一个很好的候选者。对此的get操作是否允许同时访问多个线程?我正在寻找并发非阻塞读取但原子写入。

【问题讨论】:

  • 我会非常怀疑您希望连续运行三个月的应用程序,但老实说,任何 ConcurrentMap 实现都可以在这里工作。
  • 除非加载的成本非常,否则我会使用不可变地图并每 3 个月创建一个新地图。无需担心线程安全。
  • 是的,对于所有并发集合,它的操作都是线程安全的。从javadoc中也很清楚。如果你只对 get() 感兴趣,我想知道你为什么不简单地使用 ConcurrentHashMap,它是 O(1) 而不是 O(log(n))。
  • 我认为我没有足够的数据来假设我比 OP 更了解他们的情况。
  • 老实说,我只是使用对非同步映射的 volatile 引用,并偶尔将其替换为包含新数据的副本。我真的不明白为什么我们在集合中是否有原始对象或复杂对象会很重要——这似乎与给定问题无关。如果我们有一个或一万个读者,最佳解决方案的性能应该是相同的独立的。

标签: java multithreading java.util.concurrent concurrentskiplistmap


【解决方案1】:

ConcurrentHashMap 正是您要找的。来自 Javadoc:

检索操作(包括 get)一般不会阻塞,因此可能与更新操作(包括 put 和 remove)重叠。检索反映了最近完成的更新操作在其开始时保持的结果。 (更正式地说,给定键的更新操作与报告更新值的键的任何(非空)检索具有发生前的关系。)

听起来满足了你对“并发非阻塞读但原子写”的要求。

由于您执行的写入操作很少,您可能需要指定high loadFactor and appropriate initialSize when creating the ConcurrentHashMap,这将防止在您填充地图时调整表格大小,尽管这充其量只是一个适度的好处。 (您也可以将 concurrencyLevel 设置为 1,尽管 Java 8 的 Javadoc 似乎暗示这不再用作大小提示。)

如果您绝对必须拥有SortedMapNavigableMap,那么ConcurrentSkipListMap 是开箱即用的方法。但我会在使用它们之前仔细检查您是否确实需要这些接口提供的功能(获取第一个/最后一个键、子图、查找附近的条目等)。您将付出高昂的代价(log n 与大多数操作的恒定时间)。

【讨论】:

    【解决方案2】:

    由于您正在寻找并发操作,因此您基本上有 3 个竞争对手。 Hashtable、ConcurrentHashMap、ConcurrentSkipListMap(或 Collections.synchronizedMap() 但效率不高)。

    • 在这 3 个中,后 2 个更适合并发操作,因为它们只是锁定映射的一部分,而不是像 Hashtable 那样锁定整个映射。
    • 在后 2 个中,SkipListMap 使用 skip list 数据结构,可确保平均 O(log n) 性能以实现快速搜索和各种操作。
    • 它还提供了许多 ConcurrentHashMap 无法实现的操作,例如,coilingEntry/Key()、floorEntry/Key() 等。它还维护了一个原本必须计算的排序顺序。

    因此,如果您只要求更快的搜索,我会建议 ConcurrentHashMap,但由于您还提到了“罕见的写入操作”和“所需的排序”顺序,我认为 ConcurrentSkipListMap 赢得了比赛。

    【讨论】:

      【解决方案3】:

      如果您愿意尝试第三方代码,可以考虑使用写时复制版本的地图,该版本非常适合不频繁的写入。这是通过谷歌搜索得出的:

      https://bitbucket.org/atlassian/atlassian-util-concurrent/wiki/CopyOnWrite%20Maps

      我自己从未尝试过,所以请自告奋勇。

      【讨论】:

        猜你喜欢
        • 2021-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-02
        • 1970-01-01
        • 2022-01-23
        • 2015-01-23
        • 2016-12-10
        相关资源
        最近更新 更多