Tuesday, January 26, 2016

Read / Write locks


Imagine you have an application that reads and writes some resource, but writing it is not done as much as reading it is. 

Two threads reading the same resource does not cause problems for each other, so multiple threads that want to read the resource are granted access at the same time, overlapping. But, if a single thread wants to write to the resource, no other reads nor writes must be in progress at the same time. 

To solve this problem of allowing multiple readers but only one writer, you will need a read / write lock.

Java 5 comes with read / write lock implementations in the java.util.concurrent package. Even so, it may still be useful to know the theory behind their implementation.

Read / Write Lock Java Implementation


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ThreadSafeArrayList<E>
{
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

private final Lock readLock = readWriteLock.readLock();

private final Lock writeLock = readWriteLock.writeLock();

private final List<E> list = new ArrayList<>();

public void set(E o)
{
writeLock.lock();
try
{
list.add(o);
System.out.println("Adding element by thread"+Thread.currentThread().getName());
       }
finally
{
writeLock.unlock();
}
}

public E get(int i)
{
readLock.lock();
try
{
         System.out.println("Printing elements by thread"+Thread.currentThread().getName());
       return list.get(i);
}
finally
{
readLock.unlock();
}
}
   
public static void main(String[] args)
      {
           ThreadSafeArrayList<String> threadSafeArrayList = new ThreadSafeArrayList<>();
           threadSafeArrayList.set("1");
           threadSafeArrayList.set("2");
           threadSafeArrayList.set("3");

          System.out.println("Printing the First Element : "+threadSafeArrayList.get(1));
      }
}


OUTPUT :

Adding element by threadmain
Adding element by threadmain
Adding element by threadmain
Printing elements by threadmain

Reading from List : 2

Live Scenario,


In the great majority of database applications, the frequency of read operations greatly exceeds the frequency of write operations. This is why databases implement read-write locks for their records, which allow for concurrent reading, but still demand exclusive writing. This can markedly increase performance.

Occasionally, a class may benefit as well from a read-write lock, for exactly the same reasons - reads are much more frequent than writes. As usual, you should measure performance to determine if a read-write lock is really improving performance.

Here's a sketch of how to use such locks. It uses the ReentrantReadWriteLock of the java.util.concurrent package. 

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
  User preferences, using a read-write lock. 
  
 <P>The context: preference information is read in upon startup.
 The config data is 'read-mostly': usually, a caller simply reads the 
 information. It gets updated only occasionally. 

 <P>Using a read-write lock means that multiple readers can access the 
 same data simultaneously. If a single writer gets the lock, however, then 
 all other callers (either reader or writer) will block until the lock is 
 released by the writer.

  <P>(In practice, Brian Goetz reports that the implementation of ConcurrentHashMap 
  is so good that it would likely suffice in many cases, instead of a read-write lock.)
*/
public final class Preferences {
  
  /** Fetch a setting - this is the more common operation.  */
  public String fetch(String aName){
    String result = "";
    fReadLock.lock();
    try {
      result = fPreferences.get(aName);
    }
    finally {
      fReadLock.unlock();
    }
    return result;
  }

  /** Change a setting - this is the less common operation. */
  public void update(String aName, String aValue){
    fWriteLock.lock();
    try {
      fPreferences.put(aName, aValue);
    }
    finally {
      fWriteLock.unlock();
    }
  }

  //...elided
  
  // PRIVATE
  
  /** Holds the preferences as simple name-value pairs of Strings. */
  private final Map<String, String> fPreferences = new LinkedHashMap<>();
  private final ReentrantReadWriteLock fLock = new ReentrantReadWriteLock();
  private final Lock fReadLock = fLock.readLock();
  private final Lock fWriteLock = fLock.writeLock();

}


No comments:

Post a Comment