Tuesday, November 2, 2010

Difference Between Synchronized and ReentrantLock

Gnerally we think we can improve performence using  ReentrantLock instead of synchronized block. During my understanding I found one situation where ReentrantLock was not able to replace synchronized.

ReentrantLock object can not take lock on specified object.

Example:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AtomicTest {
 public static void main(String[] args) throws Exception {
  AtomicTest at = new AtomicTest();
  SharedObject so = at.new SharedObject();
  SyncProcessor sp1 = at.new SyncProcessor(so, "One", 1500);
  SyncProcessor sp2 = at.new SyncProcessor(so, "Two", 1);
  Thread t1 = new Thread(sp1);
  Thread t2 = new Thread(sp2);
  t1.start();
  t2.start();
  Thread.sleep(3500);
  System.out.println("Final Value with Synchronized *******=" + so.getObj());

  // Start with Lock
  LockProcessor lp1 = at.new LockProcessor(so, "Three", 1500);
  LockProcessor lp2 = at.new LockProcessor(so, "Four", 1);

  Thread t11 = new Thread(lp1);
  Thread t22 = new Thread(lp2);
  t11.start();
  t22.start();
  Thread.sleep(3500);
  System.out.println("Final Value with reentrant*******=" + so.getObj());
 }

 private class SharedObject {
  private String obj;

  public String getObj() {
   return obj;
  }

  public void setObj(String obj) {
   this.obj = obj;
  }

 }
 private class SyncProcessor implements Runnable {
  private SharedObject toProcess;
  private String val;
  private int sleep;

  public SyncProcessor(SharedObject toProcess, String val, int sleep) {
   this.toProcess = toProcess;
   this.val = val;
   this.sleep = sleep;
  }

  @Override
  public void run() {
   synchronized (toProcess) {
    System.out.println("Start write" + val);
    try {
     Thread.sleep(sleep);
     toProcess.setObj(val);
    } catch (Exception ex) {
    }
    System.out.println("End write" + val);
   }

  }
 }
 private class LockProcessor implements Runnable {
  private SharedObject toProcess;
  private String val;
  private int sleep;
  private Lock lock = new ReentrantLock();

  public LockProcessor(SharedObject toProcess, String val, int sleep) {
   this.toProcess = toProcess;
   this.val = val;
   this.sleep = sleep;
  }

  @Override
  public void run() {
   lock.lock();
   System.out.println("Start write" + val);
   try {
    Thread.sleep(sleep);
    toProcess.setObj(val);
   } catch (Exception ex) {
   }
   System.out.println("End write" + val);
   lock.unlock();

  }
 }
}

Analysis of Example: In the above example there are two processors SyncProcessor and LockProcessor and both process the passed SharedObject.
When we run this prgram we get the following result.

Start writeOne
End writeOne
Start writeTwo
End writeTwo
Final Value with Synchronized *******=Two
Start writeThree
Start writeFour
End writeFour
End writeThree
Final Value with reentrant*******=Three

The differene between processors are that one is using synchronized block whereas other is using Lock mechanism.

Difference: When we are using synchronized block then we are taking lock on SharedObject's object and we can define it in synchronized but when using Lock interface we are taking lock on LockProcessor's object.

We should be careful during replacement of synchronized block with Lock.

1 comment:

  1. The implementation of LockProcessor is wrong. The member field "lock" shouldn't be a instance variable, but should be shared between LockProcessors. The best way of doing that is to put it into the SharedObject.

    ReplyDelete