Pages

19 April 2012

Why Multiple Locks Allowed on a Same Monitor?

In Java language, any class instance is associated with a monitor that is used to lock-unlock objects by threads for synchronization.While only one thread at a time is holding a lock on an object's monitor, other threads are blocked until they can obtain a lock on that monitor. It is also legal to lock an object multiple times that each of unlock action will make the state of monitor reverse. But why do we lock an object multiple times?

Object Monitors and Thread Relations
Initially, i couldn' t grasp the reason of allowing multiple locks on a montior. Since the JVM guarantees that other threads will be blocked after holding lock on the montior. It seamed to me really useless but it obviously prevents compiler from controlling nested synchronized blocks' lock objects.

In addition to that, next locking attempts of a thread that already holding lock of same monitor is harmless for other threads. Beacuase each nested synchronized code blocks must be finished before the top level synchronized code block release the monitor.

On the other hand, when a "synchronized" method of a class instance is called, the caller thread has the monitor of that instance. No other thread can execute no method of this instance but only the monitor owner can. In this point of view, a synchronized method that has combination of calls to other synchronized methods of same class will be valid.

public class MultipleLocks {

  public static void main(String[] args) {
    Sample test = new Sample();
    new Locker(test, "first").start();
    new Locker(test, "second").start();
    new Locker(test, "three").start();
    new Locker(test, "four").start();
  }
  
}

class Locker extends Thread {
  
  private Sample test;
  
  public Locker(Sample test, String name) {
    super(name);
    this.test = test;    
  }
  
  @Override
  public void run() {
    System.out.println(getName() + ".run() is started...");
    synchronized (test) {
      System.out.println(getName() + " lock on test object monitor. #1");
      // this sleep is for giving execution to other threads
      // they will printout the first statement
      // then they will be blocked to obtain monitor of test obj.
      sleepForWhile(1000); 
      synchronized (test) {
        System.out.println(getName() + " lock on test object monitor. #2");
        synchronized (test) {
          System.out.println(getName() + " lock on test object monitor. #3");
          test.notifyAll(); // it is  optional
        }  
      }  
    }
  }
  
  private void sleepForWhile(long ms) {
    try {
      Thread.sleep(ms);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class Sample { }

Possible output is like this:

first.run() is started...
first lock test object. #1
second.run() is started...
three.run() is started...
four.run() is started...
first lock test object. #2
first lock test object. #3
four lock test object. #1
four lock test object. #2
four lock test object. #3
three lock test object. #1
three lock test object. #2
three lock test object. #3
second lock test object. #1
second lock test object. #2
second lock test object. #3