Simple Java name based locks?

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
}

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);


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.


// pool of names that are being locked
HashSet<String> pool = new HashSet<String>(); 

lock(name)
    synchronized(pool)
        while(pool.contains(name)) // already being locked
            pool.wait();           // wait for release
        pool.add(name);            // I lock it

unlock(name)
    synchronized(pool)
        pool.remove(name);
        pool.notifyAll();

maybe this is useful for you: jkeylockmanager

Edit:

My initial response was probably a bit short. I am the author and was faced with this problem several times and could not find an existing solution. That's why I made this small library on Google Code.


Maybe a little later but you can use Google Guava Striped

Conceptually, lock striping is the technique of dividing a lock into many stripes, increasing the granularity of a single lock and allowing independent operations to lock different stripes and proceed concurrently, instead of creating contention for a single lock.

//init
stripes=Striped.lazyWeakLock(size);
//or
stripes=Striped.lock(size);
//...
Lock lock=stripes.get(object);