我有以下情况:我正在同时处理具有给定密钥的请求。只要正在进行的每个密钥都是唯一的,我就可以同时处理任意数量的请求。
我是Java中的并发新手。必须有一些模式/实用/现有问题,但我无法弄清楚要搜索什么。希望有人能指出我正确的方向,或评论我到目前为止所做的事情。
此类管理锁:
class LockMap<K> {
private Map<K, Object> locks = new HashMap<>();
void acquireLock(K key) throws InterruptedException {
Object lockObj;
synchronized (locks) {
lockObj = locks.get(key);
if (lockObj == null) lockObj = new Object();
locks.put(key, lockObj);
}
synchronized (lockObj) {
lockObj.wait();
}
}
void releaseLock(K key) {
Object lockObj;
synchronized (locks) {
lockObj = locks.get(key);
locks.remove(key);
}
if (lockObj != null) {
synchronized (lockObj) {
lockObj.notify();
}
}
}
}
然后我像这样使用锁管理器:
// lockMap is instance of LockMap shared across all threads
void doSomething(K key) {
lockMap.acquireLock(key);
try {
// something
} finally {
lockMap.releaseLock(key);
}
}
这是正确的方法吗?
答案 0 :(得分:3)
以下解决方案不会锁定LockMap,因此极端并行。它使用定制的Locks
来跟踪可以删除它们的时刻,并处理并发删除/创建。
class Lock {
boolean busy=true; // locked state, a thread is working
int waitCount=0; // number of waiting threads
/** returns true if lock succeeded */
synchronized boolean tryLock() throws InterruptedException {
if (busy) {
waitCount++;
} else if (waitCount==0){
// such values mean that the lock is deleted
return false;
} else {
busy=true;
return true;
}
for (;;) {
wait();
if (!busy) {
waitCount--;
busy=true;
return true;
}
}
}
}
class LockMap<K> {
private ConcurrentHashMap<K, Lock> locks = new ConcurrentHashMap<>();
void acquireLock(K key) throws InterruptedException {
for (;;) {
Lock lockObj = locks.get(key);
if (lockObj==null) {
Lock myLockObj = new Lock();
lockObj=locks.putIfAbsent(key, myLockObj);
if (lockObj==null) {
// successfully inserted, and so locked
return;
}
}
// lockObj existed, lock it or wait in queue
if (lockObj.tryLock()) {
return;
}
}
}
void releaseLock(K key) {
Lock lockObj = locks.get(key);
synchronized (lockObj) {
lockObj.busy=false;
if (lockObj.waitCount==0) {
locks.remove(key);
} else {
lockObj.notify();
}
}
}
}
答案 1 :(得分:2)
这个怎么样:
创建ConcurrentHashMap<K,Semaphore>
ConcurrentMap<K, Semaphore> myMap = new ConcurrentHashMap<>();
在您的doSomething()
方法中,使用映射的putIfAbsent()
方法向地图添加一个允许的信号量,只有在地图中不存在该键时才会显示。
随后在密钥上执行get()
以获取该密钥的信号量,然后执行您的操作。完成后释放信号量。
void doSomething(K key) {
myMap.putIfAbsent(key, new Semaphore(1));
Semaphore s = myMap.get(myKey);
s.aquire();
try {
// do stuff
} finally {
s.release();
}
}
这个方案唯一真正的问题是如果你的密钥列表会无限增长,我没有一个良好的无竞争条件的策略来从地图中删除信号量。 (但是如果你知道你会一遍又一遍地重复使用相同的密钥,或者列表会慢慢增长,那么也许这没关系。)