Intrinsic and Extrinsic locks - Java

1. Intrinsic Lock
2. Explicit Lock
Whenever we synchronize any object we get an Intrinsic Lock on that object. There is one more way to implement locking/synchronizing i.e. using Explicit Locks. From Java 1.5 java.util.concurrent.locks package provides some explicit locking classes which can be used to replace the the intrinsic locks. One of commonly used example of explicit lock is ReentrantLock.
There is a fair difference between throughput of Reentrant Lock over Intrinsic Lock in Java 1.5. That means the throughput of application using intrinsic lock goes down drastically as compared  to Reentrant Lock in case of increasing no of threads. This gap was fixed in 1.6 and now both behave quite same in even in multiple thread scenario.
This was a brief description about intrinsic and explicit locks in Java. Now there is question is when to use explicit and when to use intrinsic. There are many reasons to use explicit reasons. One of them is tryLock() method which helps to prevent the deadlock scenarion. I ll we discussing this in later blogs. But for now I am going to use a example to show ‘How can we ensure the thread scheduling fairness between threads using Explicit Locks‘.
So out problem is that in case of multiple threads asking for a shared lock, we want to ensure that the thread get the lock the longest thread in waiting on the lock object.
We will start with using intrinsic lock and see how it behaves, in case of multiple threads. In this example we have a Simple Monitor Class which we will using for Locking and it will be shared among threads.
Class : Monitor
package com.intrinsiclock;

public class Monitor {

}
As we see here it just a blank class, which we will be using to share between threads as a common lock object.
The other class in this example is out thread class:
Class : MyThread
package com.intrinsiclock;

public class MyThread implements Runnable {

private Monitor monitor;

public MyThread(Monitor monitor) {
super();
this.monitor = monitor;
}

@Override
public void run() {

printMessage("Entered run method...trying to lock monitor object");
synchronized (monitor) {
printMessage("Locked monitor object");
try{
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
printMessage("Releasing lock");
monitor.notifyAll();
}
printMessage("End of run method");

}

private void printMessage(String msg){
System.out.println(Thread.currentThread().getName()+" : "+msg);
}

}
This class has implemented run method, and get shared lock in its constructor. It tries to lock monitor using synchronise block on monitor. Once it is able to lock it sleeps for some time just to ensure other threads tries to lock this monitor. Once it wakes up it just release lock by notifying all other waiting threads. We have added simple print messages to show where the threads are and what they are doing.
And finally our main class which will invoke multiple threads contending for the locks:
Class: IntrinsicTest
package com.intrinsiclock;

public class IntrinsicTest {

public static void main(String[] args) {
System.out.println("=========Intrinsic Test=======");
String[] myThreads = {"Therad ONE","Thread TWO","Thread THREE","Thread FOUR"};
Monitor monitor = new Monitor();
for(String threadName:myThreads)
{
new Thread(new MyThread(monitor),threadName).start();
}

}

}
One we run this example we get following output:
=========Intrinsic Lock Test=======
Therad ONE : Entered run method...trying to lock monitor object
Therad ONE : Locked monitor object
Thread THREE : Entered run method...trying to lock monitor object
Thread TWO : Entered run method...trying to lock monitor object
Thread FOUR : Entered run method...trying to lock monitor object
Therad ONE : Releasing lock
Therad ONE : End of run method
Thread FOUR : Locked monitor object
Thread FOUR : Releasing lock
Thread FOUR : End of run method
Thread TWO : Locked monitor object
Thread TWO : Releasing lock
Thread TWO : End of run method
Thread THREE : Locked monitor object
Thread THREE : Releasing lock
Thread THREE : End of run method
The output of this program will be different each time we run this code. Let me explain you the reason. As we here the Thread ONE has acquired the lock in the first place and meanwhile the Thread TWO,THREE and FOUR also came in and requested for lock. The order in which they came are THREE, TWO and FOUR. But when we see the output of the code, it is FOUR who got lock first followed by TWO and THREE. So basically FOUR jumped out of line and grabbed the lock once it was released by ONE.
The reason is notifyAll(). Actually the methods notify() and notifyAll() does not guarantee which one of the waiting thread will get lock. That is the reason we will get random sequence in which the threads gets lock in this example.
This is not what we wanted to achieve, but we wanted the thread who requested for lock first should get the lock first. But it is not possible in case of using intrinsic locks. So here comes the Explicit Locks for our help. The ReentrantLock has a special flag called fairness. By making ‘fair=true‘ it ensures that the lock in granted to the thread who requested first. By default ReentrantLock fair flag is false i.e. same behavior as intrinsic lock. But by just passing one argument to the constructor to the ReentrantLock class we can achieve the fairness we are looking for.
Lock lock = new ReentrantLock(true);
Lets take a example to show this. So our first class in our thread class
Class : MyThread
package com.explicitlock;

import java.util.concurrent.locks.Lock;

public class MyThread implements Runnable {

private Lock lock;

public MyThread(Lock lock) {
super();
this.lock = lock;
}

@Override
public void run() {

printMessage("Entered run method...trying to lock monitor object");
lock.lock();
try{
printMessage("Locked monitor object");
try{
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
finally{
printMessage("Realising lock");
lock.unlock();
}
printMessage("End of run method");

}

private void printMessage(String msg){
System.out.println(Thread.currentThread().getName()+" : "+msg);
}

}
As we can see here we are using Lock object to achieve synchronization in this thread class. The lock.lock() method is a blocking method. It blocks the current thread untill it gets the lock. Once it gets the lock it does same stuff as our previous example, sleeps for some time and release lock.Interestingly we are calling unlock() in finally block, this is very important to ensure that lock is release even the thread exist due to some abnormal termination. Otherwise its very difficult to detect such leaks when a thread died with un-released lock.
Now lets see the main class which invokes multiple thread with one shared lock, which all of them tries to lock:
Class : ExplicitLockTest
package com.explicitlock;

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

public class ExplicitLockTest {

public static void main(String[] args) {
System.out.println("=========Explicit Test=======");
String[] myThreads = {"Therad ONE","Thread TWO","Thread THREE","Thread FOUR"};
Lock lock = new ReentrantLock(true);
for(String threadName:myThreads)
{
new Thread(new MyThread(lock),threadName).start();
}

}

}
Here we have created a Lock object of ReentrantLock and shared among all threads. The catch is passing fair flag while creating the lock object:
Lock lock = new ReentrantLock(true);
Now lets run the code
=========Explicit Lock Test=======
Therad ONE : Entered run method...trying to lock monitor object
Therad ONE : Locked monitor object
Thread TWO : Entered run method...trying to lock monitor object
Thread THREE : Entered run method...trying to lock monitor object
Thread FOUR : Entered run method...trying to lock monitor object
Therad ONE : Realising lock
Therad ONE : End of run method
Thread TWO : Locked monitor object
Thread TWO : Realising lock
Thread TWO : End of run method
Thread THREE : Locked monitor object
Thread THREE : Realising lock
Thread THREE : End of run method
Thread FOUR : Locked monitor object
Thread FOUR : Realising lock
Thread FOUR : End of run method
Here we can see Thread ONE has acquired the lock first meanwhile the Thread TWO, THREE and FOUR also came in and requested for the lock. So once the lock got released by ONE it was allocated to TWO followed by THREE and FOUR. And this lock ensures that the thread who requested lock first get the lock first whenever available.
The catch is
Lock lock = new ReentrantLock(true);
If we don’t pass the the fair flag here, then it behaves same as intrinsic locks. But using fair flag achieves the fairness in thread lock allocation.
Note: Using fair flag results into drop of throughput of the application. Hence we should not be using anyway but whenever the the fairness is required.

Source: https://mishrabagish.wordpress.com/2012/12/17/explicit-vs-intrinsic-locks-in-java-ensuring-thread-scheduling-fairness/

Comments

Popular posts from this blog

EJB - Stateful vs Stateless

Mirror binay tree - Java