Use Guava Striped Class
In the past I have had success using the Guava Striped class for this type of Object to Lock mapping operation. For in-depth details on the Striped class please see the Guava wiki or the Striped JavaDoc. Essentially, using a Striped<Lock> instance to index Lock values via String keys is a thread-safe way to generate Lock instances that minimizes memory footprint while maximizing concurrency. If you are avoiding 3rd party libraries you could instead implement your own Map<Object, Lock> wrapper (perhaps mixing in WeakReference) to roll your own less full-featured version of the Striped class (be sure to directly compare hashcodes instead of relying on the == operator or the equals methods!). In either case, the Striped class is a good place to start learning about how to implement your lock generation/retrieval.
Example
Note that for a specific use case, like for achieving atomic operations for database rows, you might want to use a ReadWriteLock instead of a simple/basic Lock (using Semaphore is also possible). Also, please note that we don't need to intern the String object given to Striped.get() because the Striped class compares Object's for equality using hashcodes and the String class makes special guarantees about hashcodes between character equivalent Strings always being equal. This example does use an interned String (literal Strings are automatically interned) but the Striped class works perfectly well with a dynamically generated String which is not interned.
final Striped<Lock> stripedLocks = Striped.lock(10);
final String rowID = "rowLock123";
final Lock rowLock = stripedLocks.get(rowID);
try{
rowLock.lock();//Could also use tryLock or lockInterruptibly methods
//... we are successfully in the fenced/locked code block now
//... put your concurrency sensitive code here
}finally{
rowLock.unlock();
}
Correction
Don't use synchronized on the returned Lock object obtained from Striped.get(String)!
I am adding this explicit warning to not use the returned Lock object as a synchronized block's monitor because someone edited my answer to include an example that incorrectly used the returned Lock as a monitor object.
For reference, this is what that would look like in the above example:
//DO NOT USE THE RETURNED LOCK LIKE THIS
final Lock rowLock = stripedLocks.get(rowID);
synchronized(rowLock){
//...oh no
}
You should not use synchronized on a Lock because that defeats the entire purpose of using Lock! The Lock class is intended as a replacement for the use of synchronized blocks. Using them together sacrifices the benefits of Lock while still having the headaches and gotchas of synchronized.