Explicit locks in Java 5 (ctd)

Read-write locks

In cases of shared objects where reads far outnumber writes and where accesses are relatively non-trivial, it may be worth using a read/write lock. This is a lock where, provided no thread owns the write lock, multiple threads are permitted to hold read locks simultaneously.

In the Java 5 concurrency library, a read/write lock interface has been defined and implemented by the ReentrantReadWriteLock class. The interface exposes two underlying locks for reading and writing:

public interface ReadWriteLock {
  public Lock readLock();
  public Lock writeLock();
}

These individual locks then work as described on the previous page where we described the Lock interface. For example, to protect access to an array with a read/write lock we could write something like the following:

public void ConcurrentIntegerArray {
  private final ReadWriteLock rwl =
    new ReentrantReadWriteLock();
  private final int[] arr;

  public ConcurrentIntegerArray(int size) {
    arr = new int[size];
  }
  public int get(int index) {
    Lock l = rwl.readLock();
    l.lock();
    try {
      return arr[index];
    } finally {
      l.unlock();
    }
  }
  public void set(int index, int val) {
    Lock l = rwl.writeLock();
    l.lock();
    try {
      arr[index] = val;
    } finally {
      l.unlock();
    }
  }
}

Notice how we wrap a read from the array in a lock/unlock operation on the read lock, and a write to the array with a lock/unlock on the write lock. Multiple threads will be allowed to read from the array simultaneously, but only one thread can write at any one time.

Fairness

While a thread holds the write lock, other threads are prohibited from acquiring a read lock. Conversely, a thread wanting to write must wait for all readers to relinquish their locks before being able to acquire the write lock. By default, this can mean that writing threads are heavily penalised in favour of readers: if there are several readers, it can take the writer a comparatively long time to acquire the lock. Of course, read/write locks are generally designed for cases where writes are rare compared to reads. In some cases, it may be appropriate to instantiate ReentrantReadWriteLock as "fair". In this mode, readers will be prohibited from acquiring the lock while there are threads waiting to acquire the write lock, and locks will generally be acquired in "first-come-first-served" order. As with fair locks generally, there is generally a high throughput penalty for requiring a read/write lock to be fair.