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
}
Based on the answer of McDowell and his class IdMutexProvider, I have written the generic class
LockMap
that uses WeakHashMap to store lock objects.LockMap.get()
can be used to retrieve a lock object for a key, which can then be used with the Javasynchronized (...)
statement to apply a lock. Unused lock objects are automatically freed during garbage collection.Example of how to use the LockMap class:
A simple test program for the LockMap class:
If anyone knows a better way to automatically test the LockMap class, please write a comment.
2 years later but I was looking for a simple named locker solution and came across this, was usefull but I needed a simpler answer, so below what I came up with.
Simple lock under some name and release again under that same name.
Here is the code:
I'd like to notice that
ConcurrentHashMap
has built-in locking facility that is enough for simple exclusive multithread lock. No additionalLock
objects needed.Here is an example of such lock map used to enforce at most one active jms processing for single client.
For locking on something like a user name, in-memory
Lock
s in a map might be a bit leaky. As an alternative, you could look at using WeakReferences with WeakHashMap to create mutex objects that can be garbage collected when nothing refers to them. This avoids you having to do any manual reference counting to free up memory.You can find an implementation here. Note that if you're doing frequent lookups on the map you may run into contention issues acquiring the mutex.
Can you have a
Map<String, java.util.concurrent.Lock>
? Each time you require a lock, you basically callmap.get(lockName).lock()
.Here's an example using Google Guava:
Then
lockMap.get("anyOldString")
will cause a new lock to be created if required and returned to you. You can then calllock()
on that lock.makeComputingMap
returns a Map that is thread-safe, so you can just share that with all your threads.