Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple Java name based locks?

Tags:

java

locking

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 of 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 } 
like image 441
worpet Avatar asked Apr 12 '11 18:04

worpet


People also ask

How does ReentrantLock work in Java?

A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it. A thread invoking lock will return, successfully acquiring the lock, when the lock is not owned by another thread. The method will return immediately if the current thread already owns the lock.

How many locks are there in Java?

there is two type of lock in java....


2 Answers

All those answers I see are way too complicated. Why not simply use:

public void executeInNamedLock(String lockName, Runnable runnable) {   synchronized(lockName.intern()) {     runnable.run();   } } 

The key point is the method intern: it ensures that the String returned is a global unique object, and so it can be used as a vm-instance-wide mutex. All interned Strings are held in a global pool, so that's your static cache you were talking about in your original question. Don't worry about memleaks; those strings will be gc'ed if no other thread references it. Note however, that up to and including Java6 this pool is kept in PermGen space instead of the heap, so you might have to increase it.

There's a problem though if some other code in your vm locks on the same string for completely different reasons, but a) this is very unlikely, and b) you can get around it by introducing namespaces, e.g. executeInNamedLock(this.getClass().getName() + "_" + myLockName);

like image 91
dmoebius Avatar answered Oct 15 '22 04:10

dmoebius


Can you have a Map<String, java.util.concurrent.Lock>? Each time you require a lock, you basically call map.get(lockName).lock().

Here's an example using Google Guava:

Map<String, Lock> lockMap = new MapMaker().makeComputingMap(new Function<String, Lock>() {   @Override public Lock apply(String input) {     return new ReentrantLock();   } }); 

Then lockMap.get("anyOldString") will cause a new lock to be created if required and returned to you. You can then call lock() on that lock. makeComputingMap returns a Map that is thread-safe, so you can just share that with all your threads.

like image 44
sjr Avatar answered Oct 15 '22 05:10

sjr