Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why in ReentrantReadWriteLock, should the readLock() be unlocked before writeLock().lock()?

From the ReentrantReadWriteLock class javadoc:

void processCachedData() {
  rwl.readLock().lock();
  if (!cacheValid) {
     // Must release read lock before acquiring write lock
5:   rwl.readLock().unlock();
6:   rwl.writeLock().lock();
     // Recheck state because another thread might have acquired
     //   write lock and changed state before we did.
     if (!cacheValid) {
       data = ...
       cacheValid = true;
     }
     // Downgrade by acquiring read lock before releasing write lock
14:  rwl.readLock().lock();
15:  rwl.writeLock().unlock(); // Unlock write, still hold read
  }

  use(data);
  rwl.readLock().unlock();
}

Why must we release the read lock before acquiring the write lock as written in the comment? If the current thread holds the read lock, then it should be allowed to acquire the write lock when other threads are not reading anymore, regardless of whether the current thread also holds the read lock. This is the behavior I would expect.

I would expect the lock upgrade at lines 5 and 6 and the lock downgrade at lines 14 and 15 to be done internally in the ReentrantReadWriteLock class. Why is that not possible?

In other words, I would expect the code to work fine like this:

void processCachedData() {
  rwl.readLock().lock();
  if (!cacheValid) {
     // The readlock is upgraded to writeLock when other threads 
     // release their readlocks.
     rwl.writeLock().lock();
     // no need to recheck: other threads can't have acquired  
     // the write lock since this thread still holds also the readLock!!!
     if (!cacheValid) {  
       data = ...
       cacheValid = true;
     }
     // Downgrade by acquiring read lock before releasing write lock
     rwl.writeLock().unlock(); // Unlock write, still hold read
  }

  use(data);
  rwl.readLock().unlock();
}

This looks like a much better and safer way to handle locking, doesn't it?

Can somebody explain the reason for this weird implementation? Thanks.

like image 471
Luigi R. Viggiano Avatar asked Jun 03 '13 16:06

Luigi R. Viggiano


3 Answers

The problem with upgrading a read lock to a write lock is, if two threads try to do it at the same time, it can lead to deadlock. Consider the following sequence:

  1. Thread A: acquires read lock
  2. Thread B: acquires read lock (Note that both threads now share the read lock).
  3. Thread A: tries to acquire write lock (blocks as thread B holds read lock)
  4. Thread B: tries to acquire write lock (blocks as thread A holds read lock)

Now there is a deadlock. Neither thread A or B can proceed, and neither will ever release the read lock.

like image 96
davmac Avatar answered Nov 10 '22 10:11

davmac


The Javadoc explicitly states upgrading a read lock to a write lock is not possible. The reason for that is because it can create a deadlock.

  • thread 1 acquire read lock
  • thread 2 acquire read lock
  • thread 1 ask to upgrade lock to write
  • thread 2 ask to upgrade lock to write

Both threads are now waiting on each other ... forever.

like image 44
Brian Roach Avatar answered Nov 10 '22 09:11

Brian Roach


Reentrancy allows downgrading from the write lock to a read lock, by acquiring the write lock, then the read lock and then releasing the write lock. However, upgrading from a read lock to the write lock is not possible (which results in a deadlock).

Source: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html

like image 32
Eng.Fouad Avatar answered Nov 10 '22 09:11

Eng.Fouad