Is it safe to block multiple ReentrantReadWriteLocks in one try block?

Let's say I have two critical resources: foo and bar. I protect them with ReentrantReadWriteLock s

 ReentrantReadWriteLock foo = new RRWL() ... ReentrantReadWriteLock bar = new RRWL() ... 

Most operations use only the foo OR panel, but some of them use both. Now that you are using one lock, you cannot just do this:

 void foo() { foo.writeLock().lock(); privateWorkOnFoo(); foo.writeLock().unlock(); } 

If an exception is thrown, your foo will be permanently blocked. Instead, you wrap it, for example

 void foo() { try { foo.writeLock().lock(); privateWorkOnFoo(); } finally { foo.writeLock().unlock(); } } 

But what if I need to work on both? Is it possible to place them in one block?

Option 1

 try { foo.writeLock().lock(); bar.writeLock().lock(); magic(); } finally { bar.writeLock().unlock(); foo.writeLock().unlock(); } 

Or you want each lock to block its block:

Option 2

 try { foo.writeLock().lock(); try { bar.writeLock().lock(); magic(); } finally { bar.writeLock().unlock(); } } finally { foo.writeLock().unlock(); } 

I could not be the first person who would have difficulty exploring this before ... I know that option 2 is β€œbulletproof”, but it is also much more service. Is option 1 allowed?

+6
source share
3 answers

Option 1 is fine. It is known as two lock options. If you look at LinkedBlockingQueue operations, such as deletion, it blocks putLock as well as takeLock. Here is an example of what the JDK does:

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

Option 1 is actually safer than Option 2, because if an exception is thrown in option 2, the second lock ( foo ) will not be unlocked: the unlock is not in the finally block.

Also, be very careful when manipulating two locks, because there are good chances of locking if one thread blocks foo , then bar , and the other thread blocks bar , then foo .

+4
source

According to the locking API, the lock () and unlock () methods may throw an exception. Therefore, version 1 is incorrect, as the second unlock can never be called. Version 2 is also incorrect, you should not call lock () inside the try block, because if lock.lock () throws an exception, then it will not be blocked, and you should not try to unlock it.

The correct version should be like this

  foo.writeLock().lock(); try { bar.writeLock().lock(); try { magic(); } finally { bar.writeLock().unlock(); } } finally { foo.writeLock().unlock(); } 
0
source

Source: https://habr.com/ru/post/944303/


All Articles