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.



Reentrant Lock in Java


- ReentrantLock in Java is added on java.util.concurrent package in Java 1.5 along with other concurrent utilities.
- ReentrantLock implements Lock, providing the same mutual exclusion and memory visibility guarantees as synchronized.
- Acquiring a ReentrantLock has the same memory semantics as entering a synchronized block, and releasing a ReentrantLock has the same memory semantics as exiting a synchronized block.
- And like synchronized,ReentrantLock offers reentrant locking semantics i.e. it allows a thread to recursively acquire the same lock that it is holding.

Extended capabilities include:

  • Non block structured Locking:  With intrinsic locks, acquire release pairs are block structured. A lock is always released in the same basic block in which it  was  acquired,  regardless  of  how  control  exits  the  block. However in reentrant lock you can acquire the lock in one method and release it in some other method.
  • The ability to lock interruptibly: The lockInterruptibly method allows you to try to acquire a lock while  remaining responsive to interruption. lockInterruptibly() may block if the lock is already held by another thread and will wait until the lock is acquired. This is the same as with regular lock(). But if another thread interrupts the waiting thread lockInterruptibly() will throw InterruptedException.
  • The ability to have more than one condition variable per monitor. Monitors that use the synchronized keyword can only have one. This means reentrant locks support more than one wait()/notify() queue.
  • Fairness: The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order. Programs using fair locks accessed by many threads may display lower overall throughput (i.e., are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation. Synchronized blocks are unfair.
  • Polled and Timed Lock Acquisition: ReentrantLock provides convenient tryLock() method, which acquires lock only if its available or not held by any other thread. An overloaded method of tryLock takes the time it should wait and try acquiring the lock before exiting.

 Examples:

  1. Guarding Object State Using ReentrantLock. 

    void lockTest() {
     Lock lock = new ReentrantLock();
     lock.lock();
     try {
      // update object state
      // catch exceptions and restore invariants if necessary
     } finally {
      lock.unlock();
     }
    }
    

    This code is somewhat more complicated than using intrinsic locks. The lock must be released in a finally block. Otherwise, the lock would never be released if the guarded code were to throw an exception.

  2. Polled and Timed Lock acquisition

    package com.concurrency;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ReentrantLockingDemo {
     final Lock lock = new ReentrantLock();
    
     public static void main(final String... args) {
      new ReentrantLockingDemo().go();
     }
    
     private void go() {
      new Thread(newRunable(), "Thread1").start();
      new Thread(newRunable(), "Thread2").start();
     }
    
     private Runnable newRunable() {
      return new Runnable() {
    
       @Override
       public void run() {
        do {
         try {
    
          if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
           try {
    
            System.out.println("locked thread " + Thread.currentThread().getName());
            Thread.sleep(1000);
           } finally {
            lock.unlock();
            System.out.println("unlocked locked thread " + Thread.currentThread().getName());
           }
           break;
          } else {
           System.out.println("unable to lock thread " + Thread.currentThread().getName() + " will re try again");
          }
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        } while (true);
       }
      };
     }
    }
    

    Output:
    locked thread Thread1
    unable to lock thread Thread2 will re try again
    unlocked locked thread Thread1
    locked thread Thread2
    unlocked locked thread Thread2

  3. Interruptible Lock Acquisition.

    package com.concurrency;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ReentrantLockInterruptiblyDemo {
     private Lock lock = new ReentrantLock();
     public ReentrantLockInterruptiblyDemo() {
      lock.lock();
     }
     
      public void f() {
       try {
        // This will never be available to a second task
        lock.lockInterruptibly(); // Special call
        System.out.println("lock acquired in f()");
       } catch(InterruptedException e) {
        System.out.println("Interrupted from lock acquisition in f()");
       }
      }
      public static void main(String[] args) throws Exception {
      Thread t = new Thread(new Blocked2());
      t.start();
      TimeUnit.SECONDS.sleep(1);
      System.out.println("Issuing t.interrupt()");
      t.interrupt();
     }
    }
    
    class Blocked2 implements Runnable {
     ReentrantLockInterruptiblyDemo blocked = new ReentrantLockInterruptiblyDemo();
     public void run() {
      System.out.println("Waiting for f() in BlockedMutex");
      blocked.f();
      System.out.println("Broken out of blocked call");
     }
    }
     
    

    Output:
    Waiting for f() in BlockedMutex
    Issuing t.interrupt()
    Interrupted from lock acquisition in f()
    Broken out of blocked call

  4. Using conditions in ReentrantLock.

    This Example is taken from http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Condition.html

    We have a bounded buffer which supports put and take methods. If a take is attempted on an empty buffer, then the thread will block until an item becomes available; if a put is attempted on a full buffer, then the thread will block until a space becomes available. We would like to keep waiting put threads and take threads in separate wait-sets so that we can use the optimization of only notifying a single thread at a time when items or spaces become available in the buffer. This can be achieved using two Condition instances.

    public class BoundedBuffer {
     final Lock lock = new ReentrantLock();
     final Condition notFull = lock.newCondition();
     final Condition notEmpty = lock.newCondition();
    
     final Object[] items = new Object[100];
     int putptr, takeptr, count;
    
     public void put(Object x) throws InterruptedException {
      lock.lock();
      try {
       while (count == items.length)
        notFull.await();
       items[putptr] = x;
       if (++putptr == items.length)
        putptr = 0;
       ++count;
       notEmpty.signal();
      } finally {
       lock.unlock();
      }
     }
    
     public Object take() throws InterruptedException {
      lock.lock();
      try {
       while (count == 0)
        notEmpty.await();
       Object x = items[takeptr];
       if (++takeptr == items.length)
        takeptr = 0;
       --count;
       notFull.signal();
       return x;
      } finally {
       lock.unlock();
      }
     }
    }
    





Thursday, 30 January 2014

Singleton Pattern in Java



Definition:

The singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects

  • Ensure that only one instance of a class is created.
  • Provide a global point of access to the object.

UML Diagram:



Implementation:

There are many Implementations of the Singleton pattern in java, lets have a look at all.

1. Using ENUM:


Joshua Bloch explained this approach in his Effective Java Reloaded talk at Google I/O 2008: link to video. Also see slides 30-32 of his presentation (effective_java_reloaded.pdf):


enum Singleton {
 INSTANCE;
 private final String[] favoriteBooks = { "Effective java", "Java Concurrency" };
 public void printFavorites() {
  System.out.println(Arrays.toString(favoriteBooks));
 }
}

According to Joshua Bloch: "This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton."

This implementation will:
  • Work in Multi-threaded environment without synchronizing it.
  • Provides the serialization machinery for free,

2. Lazy Initialization Holder Class/Initialization-on-demand holder idiom


In this technique we create a static class holder  which will return the singleton instance. It uses the Java class initialization guarantee where the static class holder will not get initialized till it is been referenced.This implementation is a well-performing and concurrent implementation valid in all versions of Java.


public class Something implements Serializable {
 private Something() {}
 
 private static class LazyHolder {
  private transient static final Something INSTANCE = new Something();
 }
 
 public static Something getInstance() {
  return LazyHolder.INSTANCE;
 }
 
 protected Object readResolve() {
  return Something.getInstance();
 }
}

  • The static class LazyHolder is only executed when the static method getInstance is invoked on the class Something, and the first time this happens the JVM will load and initialize the LazyHolder class. 
  • It is not sufficient merely to add implements Serializable to its declaration. To maintain the singleton guarantee, you have to declare all instance fields transient and provide a readResolve method. Otherwise, each time a serialized instance is deserialized, a new instance will be created.
  • Use this pattern if the initialization of the class is expensive and it cannot be done safely at class-loading time and the initialization is concurrent. .

3. Eager initialization


In the following implementattion the singleton object is instantiated when the class is loaded and not when it is first used, due to the fact that the instance member is declared static. This is why we don't need to synchronize any portion of the code in this case.


public class Singleton implements Serializable {
 private transient static final Singleton instance = new Singleton();

 private Singleton() {}
 public static Singleton getInstance() {
  return instance;
 }
 protected Object readResolve() {
  return instance;
 }
}

4. Lazy initialization


In this implementation the Singleton object is instantiated lazily only when getInstance method is called.

class Singleton implements Serializable {
 private transient static Singleton instance;
 private Singleton() { }
 public static Singleton getInstance() {
  if (instance == null) {
   instance = new Singleton();
  }
  return instance;
 }
 protected Object readResolve() {
  return Singleton.getInstance();
 }
}

  • This implementation will not work in Multi-Threaded environment. Use the below implementation to work with multi threaded environment.

5. Lazy initialization - Synchronized


This is Synchronized version of the Lazy Initialization technique to work in Multi-threaded environment.

class Singleton implements Serializable {
 private transient static Singleton instance;
 private Singleton() { }
 public static synchronized Singleton getInstance() {
  if (instance == null) {
   instance = new Singleton();
  }
  return instance;
 }
 protected Object readResolve() {
  return Singleton.getInstance();
 }
}

Using the synchronized keyword in this way will be extremely costly, because it's acted upon every time the getInstance() method is invoked. There is a different technique to avoid performance bottlenecks in applications, and this is to modify the code to synchronize only the assignment in the getInstance() method.

6. Double-checked locking


Double-checked locking used to reduce the overhead of acquiring a lock by first testing the locking criterion without actually acquiring the lock. Only if the locking criterion check indicates that locking is required does the actual locking logic proceed.


public class Singleton implements Serializable {
 private transient static Singleton instance;
 private Singleton() { }
 public static Singleton getInstance() {
  if(instance == null) {
   synchronized(Singleton.class) {
    if (instance == null) {
     instance = new Singleton();
    }
   }
  }
  return instance;
 }
 protected Object readResolve() {
  return Singleton.getInstance();
 }
}

Applicability & Examples

  • Logger Classes: The Singleton pattern is used in the design of logger classes. This classes are ussualy implemented as a singletons, and provides a global logging access point in all the application components without being necessary to create an object each time a logging operations is performed.
  • Configuration Classes: The Singleton pattern is used to design the classes which provides the configuration settings for an application. By implementing configuration classes as Singleton not only that we provide a global access point, but we also keep the instance we use as a cache object. When the class is instantiated( or when a value is read ) the singleton will keep the values in its internal structure. If the values are read from the database or from files this avoids the reloading the values each time the configuration parameters are used.
  •  Accessing resources in shared mode: It can be used in the design of an application that needs to work with the serial port. Let's say that there are many classes in the application, working in an multi-threading environment, which needs to operate actions on the serial port. In this case a singleton with synchronized methods could be used to be used to manage all the operations on the serial port.
  • Factories implemented as Singletons: Let's assume that we design an application with a factory to generate new objects(Acount, Customer, Site, Address objects) with their ids, in an multithreading environment. If the factory is instantiated twice in 2 different threads then is possible to have 2 overlapping ids for 2 different objects. If we implement the Factory as a singleton we avoid this problem. Combining Abstract Factory or Factory Method and Singleton design patterns is a common practice.