任意键的锁定处理程序
我有代码为任意键实现“锁定处理程序”。给定 一个 ,它确保一次只有一个线程可以该(或等于)键(此处表示调用)。key
process
externalSystem.process(key)
到目前为止,我有这样的代码:
public class MyHandler {
private final SomeWorkExecutor someWorkExecutor;
private final ConcurrentHashMap<Key, Lock> lockMap = new ConcurrentHashMap<>();
public void handle(Key key) {
// This can lead to OOM as it creates locks without removing them
Lock keyLock = lockMap.computeIfAbsent(
key, (k) -> new ReentrantLock()
);
keyLock.lock();
try {
someWorkExecutor.process(key);
} finally {
keyLock.unlock();
}
}
}
我明白,这段代码之所以能导致,是因为没有一个清晰的地图。OutOfMemoryError
我想到如何制作地图,这将积累有限的元素数量。当超过限制时,我们应该将最旧的访问元素替换为new(此代码应与作为监视器的最旧元素同步)。但我不知道如何进行回调,这将导致我超出限制。
请分享您的想法。
附言
我重新阅读了任务,现在我看到我有限制,即方法不能调用超过8个线程。我不知道它如何帮助我,但我刚刚提到它。handle
附言 2
通过@Boris蜘蛛被建议了一个很好的和简单的解决方案:
} finally {
lockMap.remove(key);
keyLock.unlock();
}
但是在Boris注意到代码之后,我们不是线程安全的,因为它会破坏行为:
让我们研究用同样关键调用的3个线程:
- 线程#1获取锁,现在更早
map.remove(key);
- 线程 #2 使用相等键调用,因此在线程 #1 释放锁定时等待。
- 然后线程 #1 执行 。在此线程#3之后调用方法。它检查映射中是否存在此密钥的锁,因此它会创建新锁并获取它。
map.remove(key);
handle
- 线程 #1 释放锁,因此线程 #2 获取它。
因此,线程 #2 和线程 #3 可以并行调用相等键。但这不应该被允许。
为了避免这种情况,在清除映射之前,我们应该阻止任何线程来获取锁,而等待集中的所有线程都不会获取并释放锁。看起来需要足够复杂的同步,这将导致算法工作缓慢。也许我们应该在地图大小超过一些有限的值时不时清除地图。
我浪费了很多时间,但不幸的是,我不知道如何实现这一目标。