3

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

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

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

4

3 回答 3

2

ConcurrentHashMap正是您正在寻找的。来自 Javadoc:

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

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

由于您执行的写入操作很少,因此您可能希望在创建 ConcurrentHashMap 时指定一个高 loadFactor 和适当的 initialSize,这将防止在您填充地图时调整表大小,尽管这充其量只是一个适度的好处。(您也可以将 concurrencyLevel 设置为 1,尽管 Java 8 的 Javadoc 似乎暗示这不再用作大小调整提示。)

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

于 2016-12-16T18:40:58.017 回答
1

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

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

我自己从来没有尝试过,所以请自告奋勇。

于 2016-12-16T19:51:07.770 回答
1

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

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

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

于 2016-12-16T19:34:19.940 回答