Saturday, May 10, 2014

Biggest Disadvantage of Lock over normal synchronized blocks

I mentioned in my other blog that I liked the lock mechanism introduced in Java 5. That holds true many of the API.  But, I have realized that there is a big flip side of the Lock mechanism over normal synchronized blocks.

If a thread acquires lock by calling lock() method of Lock and if that never releases the lock (which was supposed to be done by calling unlock() method) will make all others threads waiting for that lock starving. In fact, I am not sure if I can just call it as starving as the other threads can never acquire the lock.

I understand that, the documentation of Lock class clearly mentions this point,  not to fail calling unlock() as given below:

  Lock l = ...; 
     l.lock();
     try {
         // access the resource protected by this lock
     } finally {
         l.unlock();
     }
 
When locking and unlocking occur in different scopes, care must be taken to ensure that all code that is executed while the lock is held is protected by try-finally or try-catch to ensure that the lock is released when necessary.

How ever, how can we ensure that the developer does not miss this point. 

The good thing about normal synchronized blocks is that, once the execution of the synchronized block is completed that lock will automatically be released. One might counter argue that, what if the code inside synchronized block it self has some long running infinite loop kind of thing. But that kind of mistake is entirely different from the of mistake of missing releasing the lock specifically by the code. Calling unlock() is an additional responsibility, which is outside the business logic. 

Let me please know if you do not agree on this point. 

Tuesday, May 6, 2014

Usage of ReadWriteLock in Java Concurrency

I had a chance to work on multi-threading in Java when I was working with Siemens in 2001 where I  was part of a product development for Power Plant Automation System. I extensively used multi-threading using synchronization, wait, notify etc.

After a long time, I was going through the new developments in Java Concurrency and I really liked those features. Here is an example for usage of ReadWtiteLock in Java Concurrency.

Let me first make one thing clear: Many people might get an obvious question that why do we need read lock when reading any Resource by multiple threads can be always safe. How ever, think about a situation where a shared Resource is under modification and the same Resource is being read by another thread. Probably, it might be a good idea to stop that as the kind of modification is not known and hence the state of the Resource might be not in a good state to read either. If we want to achieve this without using Java Concurrency util Locks, I can't think of any simple mechanism to achieve this without using 'synchronize' the read and write blocks of the Resource on the same object. Using 'synchronize' on the read and write blocks would functionally work but that has a flip side. That would not allow multiple threads to read even if there is no write is taking place, which is too much to stop.

java.util.concurrency.lock,ReadWriteLock will solve the above issue. I would like to explain this with an example:

Resource.java
-----------------------------------------------------------------------------------------
public class Resource {
  public Resource() {
    super();
  }
  
  public void read() throws Exception{
    System.out.println("Thread: " + Thread.currentThread().getName() +" executing Read...");
    Thread.sleep(5000);
    System.out.println("Thread: " + Thread.currentThread().getName() +" exit Read");
  }
  
  public void write() throws Exception{
    System.out.println("Thread: " + Thread.currentThread().getName() +" executing Write...");
    Thread.sleep(5000);
    System.out.println("Thread: " + Thread.currentThread().getName() +" exit Write");
       
  }
}

---------------------------------------------------------------------------------------------

ReadThread.java
---------------------------------------------------------------------------------------------
public class ReadThread extends Thread {
  Resource r = null;
  ReadWriteLock lock = null;
  public ReadThread(String name, Resource r, ReadWriteLock lock) {
    super(name);
    this.r = r;
    this.lock = lock;
  }
  
  public void run(){
    
      //lock.readLock().lock();
      try{
          r.read();
        }
          
      catch(Exception e){
        e.printStackTrace();
      }
      /*finally{
        lock.readLock().unlock();
      }*/
    }
  
}
--------------------------------------------------------------------------------------------------

WriteThread.java
--------------------------------------------------------------------------------------------------
import java.util.concurrent.locks.ReadWriteLock;

public class WriteThread extends Thread{
  Resource r = null;
  ReadWriteLock lock = null;
  public WriteThread(String name, Resource r, ReadWriteLock lock) {
    super(name);
    this.r = r;
    this.lock = lock;
  }
  
  public void run(){
  
      //lock.writeLock().lock();
      try{
          r.write();
      
      }catch(Exception e){
        e.printStackTrace();
      }
      /*finally{
        lock.writeLock().unlock();
      }*/
    
    
  }
}

---------------------------------------------------------------------------------------------------

ThreadTest.java
---------------------------------------------------------------------------------------------------
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ThreadTest {
  public ThreadTest() {
    super();
  }
  
  public static void main(String[] args){
    Resource r = new Resource();
    ReadWriteLock lock = new ReentrantReadWriteLock();
    Thread t1 = new ReadThread("R1", r, lock);
    Thread t2= new WriteThread("W1", r, lock);
    
    Thread t3 = new ReadThread("R2",r, lock);
    
    t2.start();
    t1.start();
    t3.start();
    
  }
}
--------------------------------------------------------------------------------------------------
If you run the above program output would be some like this:

Thread: W1 executing Write...
Thread: R2 executing Read...
Thread: R1 executing Read...
Thread: R2 exit Read
Thread: R1 exit Read
Thread: W1 exit Write


This is obvious as the program does not use any kind of synchronize or lock and multiple threads are able to read and write the resource in parallel. Please note that, the code has ReadWriteLock but the lock is not used in the actual Threads (ReadThread and WriteThread). You might get different output based on which thread gets actually run. 

Now, let me change the code to use ReadWriteLock:

ReadThread.java
----------------------------------------------------------------------------------------------------
import java.util.concurrent.locks.ReadWriteLock;

public class ReadThread extends Thread {
  Resource r = null;
  ReadWriteLock lock = null;
  public ReadThread(String name, Resource r, ReadWriteLock lock) {
    super(name);
    this.r = r;
    this.lock = lock;
  }
  
  public void run(){
    
      lock.readLock().lock();
      try{
          r.read();
        }
          
      catch(Exception e){
        e.printStackTrace();
      }
      finally{
        lock.readLock().unlock();
      }
    }
  
}

-----------------------------------------------------------------------------------------------------

WriteThread.java
-----------------------------------------------------------------------------------------------------
import java.util.concurrent.locks.ReadWriteLock;

public class WriteThread extends Thread{
  Resource r = null;
  ReadWriteLock lock = null;
  public WriteThread(String name, Resource r, ReadWriteLock lock) {
    super(name);
    this.r = r;
    this.lock = lock;
  }
  
  public void run(){
  
      lock.writeLock().lock();
      try{
          r.write();
      
      }catch(Exception e){
        e.printStackTrace();
      }
      finally{
        lock.writeLock().unlock();
      }
    
  }
}
-----------------------------------------------------------------------------------------------------

No change in the TestThread.java (main program). By running the TestThread again with the above code for ReadThread and WriteThread, I have got the following output:

Thread: W1 executing Write...
Thread: W1 exit Write
Thread: R1 executing Read...
Thread: R2 executing Read...
Thread: R1 exit Read
Thread: R2 exit Read

If you notice keenly, Reader threads are not allowed to read while Writer thread was writing on the Resource. 
Please note that, you might get different output based on which thread gets to run, but one thing will be always true. Once the Write thread is writing, no Reader thread will be allowed.

Hopefully, this would be useful...