避免死锁示例

我想知道在下面的示例中避免死锁的替代方法是什么。以下示例是典型的银行帐户转帐死锁问题。在实践中解决它的更好方法是什么?

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);
               }
          }
     }
}
  

我想知道如果我在传输方法中分离嵌套锁定,死锁问题是否会得到解决,如下所示

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

答案 1

对帐户进行排序。死锁来自帐户的顺序(a,b vs b,a)。

所以试试:

 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);
           }
      }
 }

答案 2

除了排序锁定的解决方案之外,您还可以在执行任何帐户转移之前通过对私有静态最终锁定对象进行同步来避免死锁。

 class Account{
 double balance;
 int id;
 private static final Object lock = new Object();
  ....




 public static void transfer(Account from, Account to, double amount){
          synchronized(lock)
          {
                    from.withdraw(amount);
                    to.deposit(amount);
          }
     }

此解决方案存在一个问题,即专用静态锁将系统限制为“按顺序”执行传输。

另一个可能是如果每个帐户都有一个重入锁定:

private final Lock lock = new ReentrantLock();




public static void transfer(Account from, Account to, double amount)
{
       while(true)
        {
          if(from.lock.tryLock()){
            try { 
                if (to.lock.tryLock()){
                   try{
                       from.withdraw(amount);
                       to.deposit(amount);
                       break;
                   } 
                   finally {
                       to.lock.unlock();
                   }
                }
           }
           finally {
                from.lock.unlock();
           }

           int n = number.nextInt(1000);
           int TIME = 1000 + n; // 1 second + random delay to prevent livelock
           Thread.sleep(TIME);
        }

 }

此方法不会发生死锁,因为这些锁永远不会无限期地保留。如果获取了当前对象的锁,但第二个锁不可用,则会释放第一个锁,并且线程在尝试重新获取锁之前会休眠一段指定的时间。