java通过并发删除维护名称的唯一对象
我正在使用以下编程习惯用法。我保持同步 名称与对象关联的HashMap。查找 对象作为名称,我使用以下代码:
MyObject getObject(String name) {
synchronized(map) {
MyObject obj = map.get(name);
if (obj == null) {
obj = new MyObjec();
map.put(name, obj);
}
}
}
当我想专门研究这样一个物体时 将在这样的对象上使用同步:
synchronized(obj) {
/* do something exclusively on obj (work type I) */
}
到目前为止,这种方法一直运作良好,直到最近。新的 要求为I型和II型专用 作品I型将保留该对象,II型应移除 完成工作后,该对象将被删除。如果我做了什么 以下是:
synchronized(obj) {
/* do something exclusively on obj (work type II) */
}
synchronized(map) { /* not good! */
map.remove(obj);
}
我可能会授予某个对象某种类型的工作,尽管 对象已从地图中删除。所以基本上 应更换I类工作的同步(obj) 通过某种新的信号量将对象重新连接到地图 如果之前授予了II类工作。分别地 对象只应在没有同步时离开贴图 正在等待
最好是看不到物体。我会去的 使用只包含名称的API。这些物体只是 用于维护名称的某些状态。但是HashMap 在完成II类工作后,应将其从名称中解放出来 完整的。但在第一类或第二类工作中 不应该被锁定
有什么办法吗?这是已知的模式吗
再见
# 1 楼答案
延迟从地图中删除
# 2 楼答案
要求似乎是这样的:
Map<String, Object>
是缓存李>首先你需要一个
ConcurrentHashMap<String, Lock> keys
。这个Map
将存储String
键和Lock
对象之间的关系,我们将使用锁来锁定键。这允许我们替换key -> value
映射,而无需锁定整个数据Map
接下来你需要一个
ConcurrentHashMap<String, Object> data
。这个Map
将存储实际的映射之所以使用
ConcurrentHashMap
而不是普通的,是因为它是线程安全的。这意味着不需要手动同步。该实现实际上将Map
划分为多个扇区,并且只锁定执行操作所需的扇区——这使得它更加高效现在,逻辑将是
putIfAbsent
一个新的ReentrantLock
变成keys
。这将以线程安全的方式检查key
的锁是否已经存在。如果没有,将添加一个新的,否则将检索现有的。这意味着每把钥匙只有一把锁TypeII
的情况下,在完成后从data
移除映射李>代码如下所示:
我使用了
Lombok
注释来减少混乱的程度其想法是最小化或几乎消除公共资源锁定的数量,同时仍然允许
Thread
在需要时获得对特定映射的独占访问要清洁钥匙
Map
,您需要确保当前没有任何工作正在进行,并且在清洁期间没有Thread
人会尝试获取任何锁。您可以通过尝试获取相关锁,然后从密钥映射中删除映射来实现这一点——这将确保当时没有其他线程使用该锁您可以运行一个计划任务,例如,每X分钟清除地图上的20个键。如果将其实现为LRU缓存,那么它应该相当干净。谷歌番石榴提供an implementation你可以使用
# 3 楼答案
这个怎么样。。蜘蛛鲍里斯的一个稍加修改的版本
带有ConcurrentHashMap的主类,用于工作人员
工作界面
用于阻塞工作的基类。这意味着这个实例只能完成一项工作
在这里,名称将与concurrentHashMap中的键相同。 doWork一打电话,它就会锁定执行实际工作的区块
类型1和类型2实现
非阻塞工作-类型111(doWork可以执行工作,而不在线程之间共享任何信息)
最后一个块将作品加载到地图中