Tags

, , ,

In one of my previous blog I have shared a example how a deadlock can happen due to threads contending for locks occupied by other threads. In this blog I am going to discuss what can be done to prevent the such scenarios.

If the worker threads needs to acquire two or more locks to finish their execution, the chances of deadlocks is very high. The solution to prevent deadlock in such scenario is very simple and that is Lock Ordering.

Lets take a simple example of two shared objects which are used by worker threads to finish their job say A and B. If we don’t have lock ordering, and two worker Thread 1 and Thread 2 trying to acquired lock A and lock B. Thread 1 takes lock on A, and Thread 2 takes lock on B. Now Thread 1 is trying to acquire lock B which is locked by Thread 2 and Thread 2 is trying to lock A which is acquired by Thread 1. Oh..we have a deadlock.

Now if some how we enforce that “lock A” needs to be acquired before “lock B”, we can solve the problem. So every thread that needs lock A and lock B, will try to get lock A first and then will try lock B. And hence the condition where one thread has lock B and trying to get lock A will not occur, since it has to get lock A first.

Now the question is if we have a set of shared object, how can we determine the lock ordering or in which order the locks need to be acquired ?

Well we can have a custom ordering declared if we want some, but then the problem is maintaining such ordering table or list if difficult in specially enterprise application. Also the changes of someone breaking the ordering very high more due to human mistakes etc.

The best way I can suggest to solve this problem is using the identityHashCode from Java System class.

System.identityHashCode(Object x)

This method returns the hashCode of the object as would be returned by default hashCode method(say when we hadn’t overridden the hashCode method in our object).

This utility from System class can be used to order the locks. Following a sample example:

if(System.identityHashCode(lockA) < System.identityHashCode(lockB))
{
	//acquire lock A
	synchronized (lockA)
	{
		//acquire lock B
		synchronized (lockB) 
		{
			//do work
		}
		
	}
}
else
{
	//acquire lock B
	synchronized (lockB) 
	{
		//acquire lock A
		synchronized (lockA) 
		{
			//do work
		}
	}
}

Note : This is a simple example with two locks, the same can be scaled for more than 2 locks.