Pages

Friday 7 February 2014

ReentrantReadWriteLock in java


  • ReentrantLock which is explained here is efficient is many scenarios. However they follow a conservative locking strategy that prevents writer/writer and writer/reader overlap, but also prevents reader/reader overlap.
  • In many cases you want to allow multiple Threads to have simultaneous read without locking each other. In this case using ReentrantLock can have a huge performance overhead.
  • As long as each thread is guaranteed an up to date view of the data and no other thread modifies the data while the readers are viewing it, there will be no  problems.
A ReentrantReadWriteLock allow a resource can be accessed by multiple readers or a single writer at a time, but not both.

  • ReentrantReadWriteLock provides two Lock objects, one for reading and one for writing. To read data guarded by a ReentrantReadWriteLock you must first acquire the read lock, and to modify data guarded by a ReentrantReadWriteLock you must  first acquire the  write  lock. While there may appear to be two separate  locks, the read  lock and  write  lock are simply different views of an integrated read write lock object.
  • When a thread is doing a write operation, there can't be any thread doing read operations.
You should read the Java doc regarding the ReentrantReadWriteLock properties: http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html

ReentrantReadWriteLock Locking Rules

  • When the write lock is acquired, no other threads can acquire the lock in any form, whereas with a reader lock any other thread can acquire the read lock if it wants to.
  • Non Fair: The order in which threads are granted access is unspecified
  • Fair:
    • Trying to acquire a Read Lock: Will block if either the write lock is held, or there is a waiting writer thread.
    • Trying to acquire a Write Lock: Will block unless both the read lock and write lock are free.
Examples:


public class RWDictionary {
 private final Map<String, Data> m = new TreeMap<String, Data>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Data get(String key) {
        r.lock();
        try { return m.get(key); }
        finally { r.unlock(); }
    }
    public String[] allKeys() {
        r.lock();
        try { return (String[]) m.keySet().toArray(); }
        finally { r.unlock(); }
    }
    public Data put(String key, Data value) {
        w.lock();
        try { return m.put(key, value); }
        finally { w.unlock(); }
    }
    public void clear() {
        w.lock();
        try { m.clear(); }
        finally { w.unlock(); }
    }
}

In the above example we are using ReentrantReadWriteLock for concurrent access to the TreeMap. Multiple threads should be able to read & iterate over the Map simultaneously and should lock when we are performing any put operations on the Map.

Lock Downgrading

 One of the properties of the ReentrantReadWriteLock is the downgrading of the Locks. Which 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.

 Example:


public class CachedData {
 Object data;
 volatile boolean cacheValid;
 ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

 void processCachedData() {
  rwl.readLock().lock();
  if (!cacheValid) {
   // Must release read lock before acquiring write lock
   rwl.readLock().unlock();
   rwl.writeLock().lock();

   // Some processing
   
   // Downgrade by acquiring read lock before releasing write lock
   rwl.readLock().lock();
   rwl.writeLock().unlock(); // Unlock write, still hold read
  }
  rwl.readLock().unlock();
   }
}

Here, "downgrading" the lock means that if you hold the write lock, you can switch down to holding just the read lock by acquiring the read lock, then releasing the write lock. This means that you can have a thread that starts off doing something critically important (something that would prevent other threads from reading), does its work, and then switches to the lower-priority lock (the read lock) without ever being without the lock. This allows you to hold the lock continuously without getting preempted.



1 comment:

  1. A ReadWriteLock maintains a pair of associated locks -

    One for read-only operations; and
    one for writing.
    The read lock may be held simultaneously by multiple reader threads, so long as there are no writers. The write lock is exclusive.

    ReplyDelete