gpt4 book ai didi

java - 使用并发删除维护名称的唯一对象

转载 作者:行者123 更新时间:2023-12-03 22:07:04 25 4
gpt4 key购买 nike

我正在使用以下编程习惯用法。我保持同步具有名称与对象关联的 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);
}

我可能会授予某些对象某种我工作的类型,尽管对象已从 map 中移除。所以基本上I 类工作的 synchronized(obj) 应该被替换通过一些新的信号量将对象重新连接到 map 如果之前授予了 II 类作品。分别对象只应在未同步时离开 map 正在等待中。

最好是看不到物体。我会去使用仅包含名称的 API。对象只有用于维护名称的某些状态。但是 HashMap 类型 II 工作完成后应从名称中删除完全的。但是在 I 类或 II 类工作期间,HashMap不应该被锁定。

任何想法如何做到这一点?这是已知模式吗?

再见

最佳答案

需求好像是这样的:

  • 有一个Map<String, Object>那是一个缓存。
  • 池中有多个工作线程访问缓存
  • 某些类型的工作要求缓存中的对象在完成后失效

首先你需要一个ConcurrentHashMap<String, Lock> keys .这Map将存储 String 之间的关系键和 Lock我们将使用的对象锁定 key 。这允许我们替换 key -> value不锁定整个数据的映射 Map .

接下来您需要一个 ConcurrentHashMap<String, Object> data .这Map将存储实际的映射。

使用 ConcurrentHashMap 的原因而不是一个普通的是它是线程安全的。这意味着不需要手动同步。实现实际上划分了 Map进入扇区并仅锁定所需的扇区以执行操作 - 这使得效率更高。

现在,逻辑将是

  1. putIfAbsent一个新的ReentrantLock进入keys .这将以线程安全的方式检查 key 的锁是否已经存在。 .如果不是,将添加一个新的,否则将检索现有的。这意味着每把 key 只会有一把锁
  2. 获得一把锁。这意味着您获得了对映射的独占访问权。
  3. 工作。在TypeII的情况下从 data 中删除映射完成后。
  4. 打开锁。

代码看起来像这样:

private final ConcurrentHashMap<String, Object> data = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Lock> keys = new ConcurrentHashMap<>();
private final ExecutorService executorService = null; //obviously make one of these

@RequiredArgsConstructor
private class TypeI implements Runnable {

private final String key;
private final Work work;

@Override
public void run() {
final Lock lock = keys.putIfAbsent(key, new ReentrantLock());
lock.lock();
try {
final Object value = data.get(key);
work.doWork(value);
} finally {
lock.unlock();
}
}
}

@RequiredArgsConstructor
private class TypeII implements Runnable {

private final String key;
private final Work work;

@Override
public void run() {
final Lock lock = keys.putIfAbsent(key, new ReentrantLock());
lock.lock();
try {
final Object value = data.get(key);
work.doWork(value);
data.remove(key);
} finally {
lock.unlock();
}
}
}

public static interface Work {

void doWork(Object value);
}

public void doTypeIWork(final String key, final Work work) {
executorService.submit(new TypeI(key, work));
}

public void doTypeIIWork(final String key, final Work work) {
executorService.submit(new TypeII(key, work));
}

我用过Lombok注释以减少困惑。

想法是最小化或几乎消除公共(public)资源锁定的数量,同时仍然允许 Thread在需要时获得对特定映射的独占访问权。

清洁按键 Map您需要保证当前没有任何工作正在进行并且没有 Thread s 会在清理期间尝试获取任何锁。您可以通过尝试获取相关锁然后从键映射中删除映射来实现这一点 - 这将确保当时没有其他线程正在使用该锁。

您可以运行计划任务,例如每 X 分钟从 map 中清除 20 个键。如果您将它实现为 LRU 缓存,那么它应该相当干净。 Google Guava 提供an implementation你可以使用。

关于java - 使用并发删除维护名称的唯一对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22135449/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com