我正在尝试创建一个ConcurrentHashMap
支持“快照”以提供一致的迭代器,并且想知道是否有更有效的方法来做到这一点。问题是,如果同时创建了两个迭代器,那么它们需要读取相同的值,而并发哈希映射的弱一致性迭代器的定义并不能保证会是这种情况。如果可能的话,我还想避免锁定:映射中有几千个值,处理每个项目需要几十毫秒,我不想在这段时间内阻止写入器,因为这可能会导致写入器阻塞一分钟或更长时间。
到目前为止我所拥有的:
- 键是字符串,其
ConcurrentHashMap's
值是ConcurrentSkipListMap<Long, T>
- 当一个元素添加到 hashmap 时
putIfAbsent
,会分配一个新的跳过列表,并通过添加对象skipList.put(System.nanoTime(), t)
。 - 为了查询地图,我使用
map.get(key).lastEntry().getValue()
返回最新的值。为了查询快照(例如使用迭代器),我使用map.get(key).lowerEntry(iteratorTimestamp).getValue()
,其中iteratorTimestamp
是System.nanoTime()
迭代器初始化时调用的结果。 - 如果一个对象被删除,我使用
map.get(key).put(timestamp, SnapShotMap.DELETED)
,其中 DELETED 是一个静态的最终对象。
问题:
- 是否有一个库已经实现了这个?或者除此之外,是否存在比
ConcurrentHashMap
和更合适的数据结构ConcurrentSkipListMap
?我的键是可比较的,所以也许某种并发树会比并发哈希表更好地支持快照。 我如何防止这个东西不断增长?在 X 上或之前初始化的所有迭代器完成之后,我可以删除所有键小于 X 的跳过列表条目(映射中的最后一个键除外),但我不知道确定何时的好方法这已经发生了:我可以在其方法返回 false 时标记迭代器已完成
hasNext
,但并非所有迭代器都必须运行完成;我可以保留一个WeakReference
迭代器,以便我可以检测它何时被垃圾收集,但我想不出一个好的方法来检测这个,除了使用一个遍历弱引用集合然后休眠几个的线程分钟 - 理想情况下线程会阻塞WeakReference
并在包装的引用被 GC 时收到通知,但我认为这不是一个选项。ConcurrentSkipListMap<Long, WeakReference<Iterator>> iteratorMap; while(true) { long latestGC = 0; for(Map.Entry<Long, WeakReference<Iterator>> entry : iteratorMap.entrySet()) { if(entry.getValue().get() == null) { iteratorMap.remove(entry.getKey()); latestGC = entry.getKey(); } else break; } // remove ConcurrentHashMap entries with timestamps less than `latestGC` Thread.sleep(300000); // five minutes }
编辑:为了消除答案和评论中的一些混淆,我目前正在将弱一致性迭代器传递给公司另一个部门编写的代码,他们要求我提高迭代器一致性的强度。他们已经意识到我做 100% 一致的迭代器是不可行的,他们只是希望我尽最大努力。他们更关心吞吐量而不是迭代器的一致性,因此粗粒度锁不是一种选择。