- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在尝试创建一个 ConcurrentHashMap
支持“快照”以提供一致的迭代器,我想知道是否有更有效的方法来做到这一点。问题在于,如果同时创建两个迭代器,那么它们需要读取相同的值,而并发 HashMap 的弱一致性迭代器的定义并不能保证这种情况。如果可能的话,我还想避免锁定: map 中有几千个值,处理每个项目需要几十毫秒,我不想在这段时间内阻止编写器,因为这可能会导致编写器阻塞一分钟或更长时间。
我目前拥有的:
ConcurrentHashMap's
键是字符串,它的值是 ConcurrentSkipListMap<Long, T>
的实例putIfAbsent
将元素添加到 hashmap 时, 然后分配一个新的跳过列表,并通过 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
到一个迭代器,这样我就可以检测到它何时被垃圾收集,但是除了使用一个线程遍历弱引用集合然后 hibernate 几分钟之外,我想不出一种检测它的好方法 - 理想情况下线程会阻塞在 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% 一致的迭代器,他们只是希望我尽最大努力。他们更关心吞吐量而不是迭代器一致性,因此粗粒度锁不是一种选择。
最佳答案
您需要特殊实现的实际用例是什么?来自 ConcurrentHashMap 的 Javadoc (强调):
Retrievals reflect the results of the most recently completed update operations holding upon their onset. ... Iterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration. They do not throw ConcurrentModificationException. However, iterators are designed to be used by only one thread at a time.
因此常规 ConcurrentHashMap.values().iterator()
将为您提供一个“一致”的迭代器,但仅供单个线程一次性使用。如果您需要多次和/或通过多个线程使用相同的“快照”,我建议制作 map 的副本。
编辑:根据新信息和对“强一致性”迭代器的坚持,我提供了这个解决方案。请注意,使用 ReadWriteLock 具有以下含义:
一致性是通过序列化写入并在每次写入 上制作当前值的副本 来实现的。持有对“陈旧”快照的引用的读者可以继续使用旧快照而不用担心修改,垃圾收集器将在没有人使用旧快照时立即回收它。假定读者没有要求请求较早时间点的快照。
因为快照可能在多个并发线程之间共享,所以快照是只读的,不能修改。此限制也适用于从快照创建的任何 Iterator
实例的 remove()
方法。
import java.util.*;
import java.util.concurrent.locks.*;
public class StackOverflow16600019 <K, V> {
private final ReadWriteLock locks = new ReentrantReadWriteLock();
private final HashMap<K,V> map = new HashMap<>();
private Collection<V> valueSnapshot = Collections.emptyList();
public V put(K key, V value) {
locks.writeLock().lock();
try {
V oldValue = map.put(key, value);
updateSnapshot();
return oldValue;
} finally {
locks.writeLock().unlock();
}
}
public V remove(K key) {
locks.writeLock().lock();
try {
V removed = map.remove(key);
updateSnapshot();
return removed;
} finally {
locks.writeLock().unlock();
}
}
public Collection<V> values() {
locks.readLock().lock();
try {
return valueSnapshot; // read-only!
} finally {
locks.readLock().unlock();
}
}
/** Callers MUST hold the WRITE LOCK. */
private void updateSnapshot() {
valueSnapshot = Collections.unmodifiableCollection(
new ArrayList<V>(map.values())); // copy
}
}
关于java - 创建一个支持 "snapshots"的 ConcurrentHashMap,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16600019/
所以我有一个项目,我定期发布到 maven 没有问题。我现在想提供这个项目的 SNAPSHOT 版本。所以我做'mvn clean deploy'。一切正常,如下所示: [INFO] 从 sonaty
假设我有一个项目 A在依赖项目的开发中 B - 目前也在开发中,尚未发布。 所以,在 A的 POM 文件,我有以下部分: com.example project-b 1.0.0-SNAP
在我们的项目中,我们正在从 ant 脚本迁移到 gradle 构建。 我们将使用artifactory作为我们公司的 repo 管理器来存储artifacts。 我在神器中看到了一些存储库内容,其中包
我已经阅读了该网站上的许多帖子(尤其是:iOS 8 Snapshotting a view that has not been rendered results in an empty snapsho
我们将 SBT 与 sbt-git 结合使用,从 Git 修订版中获取构建版本。具体来说,我们使用 git describe 的输出作为版本号,并在当前修订未标记时附加“SNAPSHOT”限定符: v
我正在使用 bitlyj 快照 jar 从以下链接缩短 URL。 http://code.google.com/p/bitlyj/downloads/list 有人能给我解释一下snapshot.ja
我有点难以区分 SNAPSHOT 和 SNAPSHOT READ COMMITTED 之间的区别? READ COMMITTED 是一种悲观的并发方法,如何将其应用到乐观并发中?在这种情况下,在 SN
显然,如果 Artifact 在版本中没有 -SNAPSHOT,我的 Nexus 将拒绝我向他抛出的每一个部署。 数据: 失败 Artifact 的名称:entando-core-engine-exp
我有一个 UIBarButtonItem,它会打开一个像这样的弹出窗口: @IBAction func openAdmin(sender: UIBarButtonItem) { let ale
我能够以 快照 的形式获取我感兴趣的对象,如 CodePen 所示 以下是代码片段: $scope.post = {}; var postsRef = new Firebase('ht
我只在 iOS 7 中遇到此错误并且应用程序崩溃了。在 iOS 6 中,我从来没有收到任何错误,只有一次在打开相机时出现内存警告。 Snapshotting a view that has not b
我在我的快照版本(例如dependency-lib)上使用“mvn clean deploy”执行maven构建。构建成功, Artifact 成功部署在 Artifact 中。 然后,我在我的 de
我正忙于 iOS7 中的 UICollectionView。 我要在两种不同的布局之间更改我的 Collection View 的布局。它们是 UICollectionViewFlowLayout 的
我从 eclipse git 中查看了最新的源代码:git://git.eclipse.org/gitroot/platform/eclipse.platform.releng.aggregator.
我在 Amazon EC2 上有一个 postgresql 数据库,需要确定备份这些数据的最佳方式。我正在考虑两种选择: (1) 将 EBS 卷挂载到/pgsqldata 等目录,并将此目录用作 po
当我使用Jasper作为接口(interface)时,有时接口(interface)会调用jasper并生成报告,然后我们会得到如下异常: ==============================
我正在使用 Version Maven Plugin插件 use-latest-versions将 groupID=com.example* 内部依赖版本更新到最新版本的功能。这是使用 Jenkins
对于 SQL Server 2008 R2 中的 SNAPSHOt 隔离级别,MSDN ADO.Net 文档中提到了以下内容: Transactions that modify data do not
我在 Bitbucket 中创建了一个公共(public)存储库来为我保存所有版本和快照版本。 我正在使用 wagon-git 将 jar 上传到发布存储库。 这是我在 pom.xml 中的条目:
When starting the server, refuses to load my plugin with an error:启动服务器时,拒绝加载我的插件,并出现错误: Could n
我是一名优秀的程序员,十分优秀!