Java – is it safe to lock multiple reentrantreadwritelocks in the same try block?
Suppose I have two key resources, Foo and bar I protected them with some reentrantreadwritelock
reentrantreadwritelock foo = new RRWL() ... reentrantreadwritelock bar = new RRWL() ...
Most operations use only foo or bar, but some of them happen to use both Now when using a single lock, you can't just do this:
void foo() {
foo.writeLock().lock();
privateWorkOnFoo();
foo.writeLock().unlock();
}
If you throw an exception, your foo will be locked forever Instead, you wrap it up, like
void foo() {
try {
foo.writeLock().lock();
privateWorkOnFoo();
} finally { foo.writeLock().unlock(); }
}
But what if I need to work at the same time? Is it safe to put them in one block?
Option 1
try {
foo.writeLock().lock();
bar.writeLock().lock();
magic();
} finally {
bar.writeLock().unlock();
foo.writeLock().unlock();
}
Or is it necessary to provide each lock with its own block:
Option 2
try {
foo.writeLock().lock();
try {
bar.writeLock().lock();
magic();
} finally {
bar.writeLock().unlock();
}
} finally {
foo.writeLock().unlock();
}
I can't be the first person to investigate this... I know option 2 has "bulletproof", but it's also a considerable amount of maintenance Is option 1 acceptable?
Solution
Option 1 is OK It is called two locking variants If you look at the linkedblockingqueue operation, such as remove, it will lock putlock and takelock The following is a functional example of JDK:
public boolean remove(Object o) {
if (o == null) return false;
fullyLock();
try
{
// ...
}
finally {
fullyUnlock();
}
}
/**
* Lock to prevent both puts and takes.
*/
void fullyLock() {
putLock.lock();
takeLock.lock();
}
/**
* Unlock to allow both puts and takes.
*/
void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}
