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