Avoid Deadlock example

peter picture peter · Nov 10, 2012 · Viewed 10.7k times · Source

I am wondering what are the alternative ways to avoid deadlock in the following example. The following example is a typical bank account transferring deadlock problem. What are some of the better approaches to solve it in practice ?

class Account {
     double balance;
     int id;
     public Account(int id, double balance){
          this.balance = balance;
          this.id = id;
     }
     void withdraw(double amount){
          balance -= amount;
     } 
     void deposit(double amount){
          balance += amount;
     }
}
class Main{
     public static void main(String [] args){
           final Account a = new Account(1,1000);
           final Account b = new Account(2,300);
           Thread a = new Thread(){
                 public void run(){
                     transfer(a,b,200);
                 }
           };
           Thread b = new Thread(){
                 public void run(){
                     transfer(b,a,300);
                 }
           };
           a.start();
           b.start();
     }
     public static void transfer(Account from, Account to, double amount){
          synchronized(from){
               synchronized(to){
                    from.withdraw(amount);
                    to.deposit(amount);
               }
          }
     }
}
  

I am wondering if the deadlock issue will be solved if I separate the nested lock out in my transfer method like the following

 synchronized(from){
      from.withdraw(amount);
 }
 synchronized(to){
      to.deposit(amount);
 }

Answer

Will Hartung picture Will Hartung · Nov 11, 2012

Sort the accounts. The dead lock is from the ordering of the accounts (a,b vs b,a).

So try:

 public static void transfer(Account from, Account to, double amount){
      Account first = from;
      Account second = to;
      if (first.compareTo(second) < 0) {
          // Swap them
          first = to;
          second = from;
      }
      synchronized(first){
           synchronized(second){
                from.withdraw(amount);
                to.deposit(amount);
           }
      }
 }