A simple way to control access to some object between threads and have an infinite supply of such objects is to use a striped lock.
The striped lock is an array of locks (or just objects).
So based on the hash of the object you determine the index within this array of locks and then acquire it.
Example illustrating the approach:
// e.g. part of a constructor
locks = new Object[1024];
for(int k=0;k<locks.length;k++){
locks[k]=new Object();
}
// part of the processing function
int hash = requestId.hashCode();
int lockIndex = hashToIndex(hash, locks.length);
synchronized(locks[lockIndex)){
... do your stuff
}
public static int hashToIndex(int hash, int length) {
if(length <= 0) {
throw new IllegalArgumentException();
}
if (hash == Integer.MIN_VALUE) {
return 0;
}
return abs(hash) % length;
}
The big advantage of a striped lock is that you don't need to deal with the destruction of locks since they can be kept for the duration of the program. So you don't get any litter and you have nice and simple code.
This is a very basic solution to the program; if the locks are be kept for a long period, then this can lead to contention (even false contention because different requestIds map to the same lock).
In that case, restructuring the program might be a better solution. E.g. you could have an array of single thread executors and toss the requestId into the executor, based on hash of the requestId with the above hashToIndex function. This way the same requestId is guaranteed to be processed by the same thread and hence no locking is needed.