【发布时间】:2013-05-12 02:08:25
【问题描述】:
我正在尝试创建一个支持“快照”的ConcurrentHashMap,以提供一致的迭代器,我想知道是否有更有效的方法来做到这一点。问题是,如果同时创建了两个迭代器,那么它们需要读取相同的值,而并发哈希映射的弱一致性迭代器的定义并不能保证会是这种情况。如果可能的话,我还想避免锁定:映射中有几千个值,处理每个项目需要几十毫秒,我不想在这段时间内阻止写入器,因为这可能会导致写入器阻塞一分钟或更长时间。
到目前为止我所拥有的:
-
ConcurrentHashMap's键是字符串,它的值是ConcurrentSkipListMap<Long, T>的实例 - 当使用
putIfAbsent将元素添加到hashmap 时,会分配一个新的skiplist,并通过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 的所有跳过列表条目(映射中的最后一个键除外),但我不知道确定何时的好方法这已经发生了:当迭代器的
hasNext方法返回 false 时,我可以标记它已完成,但并非所有迭代器都必须运行完成;我可以将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 }
编辑:为了澄清答案和 cmets 中的一些混淆,我目前正在将弱一致的迭代器传递给公司另一个部门编写的代码,他们要求我增加强度迭代器的一致性。他们已经意识到我做 100% 一致的迭代器是不可行的,他们只希望我尽最大努力。他们更关心吞吐量而不是迭代器的一致性,因此粗粒度锁不是一种选择。
【问题讨论】:
标签: java multithreading algorithm data-structures snapshot