Page 1 of 1

Dealing with Java concurrency and deadlock problems Rate Topic: ****- 1 Votes

#1 guido-granobles  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 171
  • View blog
  • Posts: 617
  • Joined: 02-December 09

Posted 12 February 2012 - 09:11 PM

It's known by many programmers that the use of the key word 'synchronized' is either for establishing methods or establishing synchronized code blocks that affect the optimal performance of the applications with regard to process speed. Although misuse of synchronized code blocks is forbidden. Constant improvements of the JVM through the years has made the percentage of performance affected versus the benefits to resolve concurrency problems tilt the balance towards the latter. The main issue associated with the misuse of synchronized code blocks is, if we increase such methods or blocks in an application then the greater the chance that at some point in the program's execution, DeadLock may occur and the application starts an infinite loop and finally is forced to restart.

In particular, DeadLock happens when at least two threads try to lock on an object at the same time. In order to be more clear I will give an example: Thread 'A' gets the 'Lock' of the object 'obj1', while thread 'B' gets the 'Lock' of the object 'obj2'. Then thread 'A' tries to get the 'Lock' of 'obj2' before releasing the 'Lock' of 'obj1'. At the same time, thread 'B' tries to get the 'Lock' of 'obj1' before releasing the 'Lock'of 'obj2' . What we have in here is thread 'A' will wait until the thread 'B' releases the 'Lock'of 'obj2' while thread 'B' is waiting for thread 'A' to release the 'Lock' of 'obj1' . So they will be waiting on each other until the judgement day. Let's see the example:

public static Object obj1 = new Object();
 public static Object obj2 = new Object();
 public void metodo1() {
   synchronized (obj1) {
     synchronized (obj2) {
        process1();
     }
    }
 }
public void metodo2{
    synchronized (obj2) {
     synchronized (obj1) {
         process2();
     }
  }
}



As You can see there are two synchronized code blocks in two methods. First thread 'A' invokes to 'method1' and gets the 'Lock' of the object 'obj1', at the same time thread 'B' gets the 'Lock' of the object 'obj2'. Then thread 'A' tries to get the 'Lock' of the 'obj2', but it turns out that thread 'B' already has 'obj2' so thread A will have to wait until 'obj2' is released. But the problem is that thread 'B' is waiting for the release of the object 'obj1' before releasing 'obj2'. Hence DeadLock and both threads will be in an eternal wait.

Knowing when a 'Deadlock' will happen is very difficult because of the low possibility that two threads will gain access to both methods simultaneously. However, it can happen without warning. It may be just once during a period of time or it may happen many times within the same period. To detect this type of problem by just analyzing the code can be difficult. A mistake as mentioned above can be detected easily and infer that the problem can be solved by chaning the order in which the objects are locked. In other words, they must be locked in the same order in both methods in such a way that the 'Deadlock' is not possible. Let's see an example more complex:

public class Deadlock {
   static class Auto {
    private final String model;
    private final long miles;
    public Auto(String model, long ml) {
      this.model = model;
      this.miles = ml;
    }
    public String getModel() {
      return this.model;
         public synchronized long difMiles(Auto auto) {
      System.out.println("Calculate Difference in miles ");
            return Math.abs((auto.getMiles() - getMiles()))
     }
    public synchronized long getMiles() {
      System.out.println(" Get miles for " + getModel());
      return miles;
    }
  }
  public static void main(String[] args) {
    final Auto mazda = new Auto("Mazda", 60000);
    final Auto renault = new Auto("Renault", 80000);
    new Thread(new Runnable() {
      public void run() { System.out.println("Difference in miles "
               + mazda.difMiles(renault)); }
    }).start();
    new Thread(new Runnable() {
      public void run() { System.out.println("Difference in miles "
               + renault.difMiles(mazda)); }
    }).start();
  }
}



As you can see two instances of Auto class were created. The Auto class have a method which calculate the difference between two cars by miles. When the first thread is executed the Mazda object invokes difMiles method and gains the Lock of the object Mazda. On the other hand, the second thread happens at the same time and this object gains Lock of the 'Renault' object. But what happens when both threads invoke getMiles method using the object that has been passed as a prameter?.

The first thread tries to gain the Lock of 'Renault' object while the second thread tries to gain the Lock on the 'Mazda' object. As you can see, neither thread is going to get what they want and the application will get hung up. It is exactly like the first case we looked at. It is just that this time it is not so obvious and truly things could get worse when there are more lines of code. This case is a seed to germinate a potential 'DeadLock'. If you executed this code 10 consecutive times, it will probably do fine for 6 or 7 of those times, but at least 3 or 4 times it will make the application get hung up.

The fact that the application gets hung up only sometimes, makes it more difficult to find the cause of the problem. You might think there is no reason in real life for two threads to start doing such operations, therefore, let's see a typical example a little more close to real life. Let's suppose that we have to do a transfer of money between two Cashier accounts. We will have two objects 'account1' and 'account2' which are, in this instance, of the same Account Class. The code would go like this:

public class Deadlock {
   static class Cashier {
    double balance;
    public Cashier(double balInit) {
      this.balance = balInit;
    }
    public void debit(double val) {
      balance += val;
    }
     public void credit(double val) {
       balance -= val;
     }
     public double getBalance() {
       return balance;
     }
   }
   static class OperateCashier{ 
      public void transfer(Cashier from, Cashier to, double val) {
        System.out.println("balance transfer...");
         System.out.println("lock acquired from...");
        synchronized (from) {
            System.out.println("lock acquired to...");                  
          synchronized (to) {
           if (from.getBalance() >= val) {
             from.debit(val);
             to.credit(val);
             System.out.println("transfer finished...");
           }
         }
       }
      }
   }
   public static void main(String[] args) {
     final Cashier cashier1 = new Cashier(60000);
     final Cashier cashier2 = new Cashier(80000);
     final OperateCashier opc = new OperateCashier(); 
     new Thread(new Runnable() {
       public void run() {
         opc.transfer(cashier1, cashier2, 20000);
       }
     }).start();
     new Thread(new Runnable() {
       public void run() {
         opc.transfer(cashier2, cashier1, 10000);
       }
     }).start();
   }
}



The messages printed in every step are there, not just to show us what is happening during execution, but also to delay a little of the execution of the threads as it would happen in a real application which responds to multiple calls. As expected, this code is intended to get stuck in a 'DeadLock' at some point of its life cycle. This is because different threads can call the same method with parameters ordered in a different way, such as in previous examples.

Well, now the question we've been raiding is how to prevent such problems from happening when we need to synchronize access to an object?. One of the possible solutions is to carefully sort the way in which the synchronizaed methods are called from different threads or the order in which parameters are passed to methods containing synchronized code blocks. In order to do this, we must observe in what order the locks are obtained within the method. This is usually a very hard task when dealing with very complex applications. Fortunately, more recent versions of Java language give us a package which help us deal with these kinds of problems. I am talking about package java.util.concurrent.locks.

This package gives us an interface called 'Lock' which is implemented by the ReentrantLock class. This kind of object works very similar to synchronized code blocks, as we can have just one 'Lock' at the same time. Its main advantage is that it allows us to schedule alternative executions if the 'Lock' object has been obtained by another thread.

The most important method of this object is the method tryLock, which will try to get the 'lock' of the object and if it is not possible it will return false.
This will give us the possibility to write code in order to take other actions. That's its main advantage with regard the synchronized code blocks, in which an infinite loop is started until it can get the 'lock'.

Now we are going to use this kind of object in order to solve the deadlock problems in the previous example. We are going to have one Cashier class and one OperateCash class, we are going to create 2 objects from the Cashier class whose methods will be invoke in the OperateCashier class. This is the code example:

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

public class Deadlock {
 
    static class Cashier {
 
       private double balance;
       private final String name;
       public final Lock lock = new ReentrantLock();
 
       public Cashier(double balanceIni, String name) {
    	    this.balance = balanceIni;
	    this.name = name;
	}
 
        public void debit(double value) {
   	    balance += value;
	}
 
	public void credit(double value) {
	     balance -= value;
	}
 
	public double getBalance() {
	   return balance;
	}
 
	public String getName() {
	    return name;
	}
 
    }
 
    static class OperateCashier {
 
        public boolean transfer(Cashier cashierFrom, Cashier cashierTo,
				double value, String h){
 
   	     Boolean lock1 = false;
	     Boolean lock2 = false;
 
	     System.out.println("Thread " + h + ": transfer cash from " + cashierFrom.getName() +
			" to " + cashierTo.getName());
	     try {
		System.out.println("Thread " + h
		        + ": get lock "
			+ cashierFrom.getName());
		lock1 = cashierFrom.lock.tryLock();
		System.out.println("Thread " + h
		        + ": get lock "
		        + cashierTo.getName());
		lock2 = cashierTo.lock.tryLock();
	     } finally {
		 if (!(lock1 && lock2)) {
		    if (lock1) {
			cashierFrom.lock.unlock();
		    }
		    if (lock2) {
			cashierTo.lock.unlock();
		    }
	      }
	}
 
	if (lock1 && lock2) {
		try {
  		      if (cashierFrom.getBalance() >= value) {
			  cashierFrom.debit(value);
			  cashierTo.credit(value);
			  System.out.println("Thread " + h
			  + ": transfer finished...");
			}
		} finally {
			cashierFrom.lock.unlock();
			cashierTo.lock.unlock();
		}
	} else {
		System.out.println("Thread " + h
		+ ":It was not able to get the lock from both objects");
	}
	return (lock1 && lock2);
     }
   }
 
   public static void main(String[] args) {
 
	final Cashier cashier1 = new Cashier(60000, "CJ1");
	final Cashier cashier2 = new Cashier(80000, "CJ2");
	final OperateCashier opc = new OperateCashier();
	new Thread(new Runnable() {
		String nameThread = "H1";
		boolean go = false;
		long time = 100;
		public void run() {
   		   while (!go) {
			go=opc.transfer(cashier1,cashier2,20000,nameThread);
			if (!go) {
		  	   try {
			       System.out.println("Thread "+ nameThread
				+ ": Wating " + time);
				Thread.sleep(time);
  			   } catch (InterruptedException e) {}
			}
		    }
		}
	  }).start();
 
	  new Thread(new Runnable() {
	      String nameThread = "H2";
	      boolean go = false;
	      long time = 100;
 
	      public void run() {
		 while (!go) {
  		     go=opc.transfer(cashier2, cashier1, 10000, nameThread);
			if (!go) {
			   try {
		 	       System.out.println("Thread "+nameThread
                               + ": Wating " + time);
                               Thread.sleep(time);
 			   } catch (InterruptedException e) {}
			}
		 }
	      }
	   }).start();
	}
}



In the Cashier class in line number 10 a 'Lock' object has been created which is the object that will be used in order to lock the object thus preventing the other thread to use it. So when a Thread needs to use a Cashier object, it will invoke the method tryLock of the object Lock, this method will check if other thread have already locked the object, if so then it will return false, if not, it will lock the object and will return true.

The OperateCashier class have a method named 'transfer', this method makes money transfers from one cashier to another cashier, the two objects which represent those cashiers are given as parameters and so also the value to transfer and a String value that will tell us which Thread is running at the moment.

In order for the transfer operation be sucessfull, it is needed that none other thread already be making the transfer. So it will be necessary lock both cashiers. Thus any other thread will have to wait until the current transaction is complete. In line number 49 we try to get the 'lock' of the cashier which is going to make the transfer, while in line number 53 we try to do the same with the cashier that will receive the transfer. The checking whether both cashier has been locked or not is done inside of the finally block, because in case it's not possible to lock both cashiers, it may be that at least one of them has been already locked so it should be released, we do not want an object locked for ever.

In line number 65 again we check whether or not we managed lock both objects. If so then we go ahead and make the money transfer. Then we must unlock both
objects. Finally a boolean value is returned pointing out if the transfer was sucessfull.

Now let's see what is happening in the method main. The two Threads that will invoke the method transfer are created in there. Each one of these threads will try to make a money transfer. The first, from cashier1 to cashier2 and the second from cashier2 to cashier1. It's for sure that in some point during execution, one thread will find that other thread have already locked one or both cashiers, but it won't be a problem because in that case the method will return false and then we can decide what to do. In this case we will have a while loop trying to complete the transaction every 100 ms. eventually it will have its chance but in the real life you could decide to make other things.

if we execute this program many times. We can see that always both transaction are completed successfully, in whatever order, preventing a deadlock.

So this is one of the ways to reduce deadlocks in programs.

Is This A Good Question/Topic? 3
  • +

Page 1 of 1