I have the following situation: I\'m concurrently processing requests that have a given key. I can process any number of requests at the same time, as long as each key in pr
How about this:
create a ConcurrentHashMap<K,Semaphore>
ConcurrentMap<K, Semaphore> myMap = new ConcurrentHashMap<>();
in your doSomething()
method, use the putIfAbsent()
method to of your map to add a semaphore with one permit to the map, only if the key does not exist in the map.
subsequently do a get()
on the key to fetch the semaphore for that key, and then do your stuff. Release the semaphore when done.
void doSomething(K key) {
myMap.putIfAbsent(key, new Semaphore(1));
Semaphore s = myMap.get(myKey);
s.aquire();
try {
// do stuff
} finally {
s.release();
}
}
The only real problem with this scheme is if your list of keys will grow indefinitely, I don't have a good race-condition-free strategy for removing the semaphore from the map. (But if you know you will reuse the same keys over and over, or the list will grow slowly, then maybe this is ok.)
Following solution does not locks LockMap and so is extremly parallel. It uses custom-made Locks
to track the moment when they can be deleted, and handles concurrent deletion/creation.
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();
}
}
}
}