MySQL has a handy function:
SELECT GET_LOCK("SomeName")
This can be used to create simple, but very specific, name based locks for an application. However, it requires a database connection.
I have many situations like:
someMethod() {
// do stuff to user A for their data for feature X
}
It doesn't make sense to simply synchronize this method, because, for example, if this method is called for user B in the meantime, user B does not need to wait for user A to finish before it starts, only operations for the user A and feature X combination need to wait.
With the MySql lock I could do something like:
someMethod() {
executeQuery("SELECT GET_LOCK('userA-featureX')")
// only locked for user A for their data for feature X
executeQuery("SELECT RELEASE_LOCK('userA-featureX')")
}
Since Java locking is based on objects, it seems like I would need to create a new object to represent the situation for this lock and then put it in a static cache somewhere so all the threads can see it. Subsequent requests to lock for that situation would then locate the lock object in the cache and acquire its lock. I tried to create something like this, but then the lock cache itself needs synchronization. Also, it is difficult to detect when a lock object is no longer being used so that it can be removed from the cache.
I have looked at the Java concurrent packages, but nothing stands out as being able to handle something like this. Is there an easy way to implement this, or am I looking at this from the wrong perspective?
Edit:
To clarify, I am not looking to create a predefined pool of locks ahead of time, I would like to create them on demand. Some pseudo code for what I am thinking is:
LockManager.acquireLock(String name) {
Lock lock;
synchronized (map) {
lock = map.get(name);
// doesn't exist yet - create and store
if(lock == null) {
lock = new Lock();
map.put(name, lock);
}
}
lock.lock();
}
LockManager.releaseLock(String name) {
// unlock
// if this was the last hold on the lock, remove it from the cache
}
TreeMap because in HashMap size of inner array can only increase
I've created a tokenProvider based on the IdMutexProvider of McDowell. The manager uses a
WeakHashMap
which takes care of cleaning up unused locks.TokenManager:
Usage:
or rather use Integers?
A generic solution using java.util.concurrent
(4 years later...) My answer is similar to user2878608's but I think there are some missing edge cases in that logic. I also thought Semaphore was for locking multiple resources at once (though I suppose using it for counting lockers like that is fine too), so I used a generic POJO lock object instead. I ran one test on it which demonstrated each of the edge cases existed IMO and will be using it on my project at work. Hope it helps someone. :)
Maybe something like that:
I didn't test it though.
Many implementations but non similar to mine.
Called my Dynamic lock implementation as
ProcessDynamicKeyLock
because it's a single process lock, for any object as key (equals+hashcode for uniqueness).TODO: Add a way to provide the actual lock, for example,
ReentrantReadWriteLock
instead ofReentrantLock
.Implementation:
Simple test: