Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 避免死锁示例_Java_Multithreading_Parallel Processing_Deadlock - Fatal编程技术网

Java 避免死锁示例

Java 避免死锁示例,java,multithreading,parallel-processing,deadlock,Java,Multithreading,Parallel Processing,Deadlock,我想知道在下面的示例中,有哪些替代方法可以避免死锁。下面的示例是一个典型的银行帐户转移死锁问题。在实践中有哪些更好的解决方法 class Account { double balance; int id; public Account(int id, double balance){ this.balance = balance; this.id = id; } void withdraw(double a

我想知道在下面的示例中,有哪些替代方法可以避免死锁。下面的示例是一个典型的银行帐户转移死锁问题。在实践中有哪些更好的解决方法

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

对账目进行分类。死锁来自帐户的顺序(a、b与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);
           }
      }
 }
公共静态作废转账(账户来源、账户目的、双倍金额){
账户优先=从;
第二个帐户=到;
如果(第一个比较到(第二个)<0){
//交换
第一个=到;
秒=从;
}
已同步(第一){
已同步(秒){
提取(金额);
存款(金额);
}
}
}

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

 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);
          }
     }
此解决方案存在一个问题,即私有静态锁限制系统“按顺序”执行传输

如果每个帐户都有ReentrantLock,则可以使用另一个:

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

 }

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

这是一个经典问题。我认为有两种可能的解决办法:

  • 对id低于另一个帐户的帐户进行排序和同步。 这种方法在《并发圣经》第10章Java并发实践中提到。在本书中,作者使用系统哈希代码来区分帐户。看
  • 您提到了第二种解决方案-是的,您可以避免嵌套的同步块,并且您的代码不会导致死锁。但在这种情况下,处理过程可能会出现一些问题,因为如果您从第一个帐户提款,第二个帐户可能会被锁定很长时间,并且您可能需要将资金放回第一个帐户。这并不好,因为嵌套同步和两个帐户的锁定是更好、更常用的解决方案

  • 您必须满足三个要求:

  • 将一个账户的内容持续减少指定金额
  • 按规定金额持续增加其他账户的内容
  • 如果上述一项成功,另一项也必须成功
  • 你可以达到1。二,。通过使用,但您必须使用其他
    double
    ,因为没有
    AtomicDouble
    AtomicLong
    可能是您的最佳选择

    因此,剩下的是第三个要求——如果一个成功,另一个必须成功。有一种简单的技术可以很好地与原子学配合使用,那就是使用
    getandad
    方法

    class Account {
      AtomicLong balance = new AtomicLong ();
    }
    
    ...
    Long oldDebtor = null;
    Long oldCreditor = null;
    try {
      // Increase one.
      oldDebtor = debtor.balance.getAndAdd(value);
      // Decrease the other.
      oldCreditor = creditor.balance.gtAndAdd(-value);
    } catch (Exception e) {
      // Most likely (but still incredibly unlikely) InterruptedException but theoretically anything.
      // Roll back
      if ( oldDebtor != null ) {
        debtor.getAndAdd(-value);
      }
      if ( oldCreditor != null ) {
        creditor.getAndAdd(value);
      }
      // Re-throw after cleanup.
      throw (e);
    }
    

    您还可以为每个帐户(在Account类中)创建单独的锁,然后在执行事务之前获取两个锁。看一看:

    private boolean acquireLocks(Account anotherAccount) {
            boolean fromAccountLock = false;
            boolean toAccountLock = false;
            try {
                fromAccountLock = getLock().tryLock();
                toAccountLock = anotherAccount.getLock().tryLock();
            } finally {
                if (!(fromAccountLock && toAccountLock)) {
                    if (fromAccountLock) {
                        getLock().unlock();
                    }
                    if (toAccountLock) {
                        anotherAccount.getLock().unlock();
                    }
                }
            }
            return fromAccountLock && toAccountLock;
        }
    
    拿到两把锁后,你就可以不用担心安全了

        public static void transfer(Acc from, Acc to, double amount) {
            if (from.acquireLocks(to)) {
                try {
                    from.withdraw(amount);
                    to.deposit(amount);
                } finally {
                    from.getLock().unlock();
                    to.getLock().unlock();
                }
            } else {
                System.out.println(threadName + " cant get Lock, try again!");
                // sleep here for random amount of time and try do it again
                transfer(from, to, amount);
            }
        }
    

    下面是所述问题的解决方案

    import java.util.Random;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class FixDeadLock1 {
    
        private class Account {
    
            private final Lock lock = new ReentrantLock();
    
            @SuppressWarnings("unused")
            double balance;
            @SuppressWarnings("unused")
            int id;
    
            public Account(int id, double balance) {
                this.balance = balance;
                this.id = id;
            }
    
            void withdraw(double amount) {
                this.balance -= amount;
            }
    
            void deposit(double amount) {
                balance += amount;
            }
        }
    
        private class Transfer {
    
            void transfer(Account fromAccount, Account toAccount, double amount) {
                /*
                 * synchronized (fromAccount) { synchronized (toAccount) {
                 * fromAccount.withdraw(amount); toAccount.deposit(amount); } }
                 */
    
                if (impendingTransaction(fromAccount, toAccount)) {
                    try {
                        System.out.format("Transaction Begins from:%d to:%d\n",
                                fromAccount.id, toAccount.id);
                        fromAccount.withdraw(amount);
                        toAccount.deposit(amount);
                    } finally {
                        fromAccount.lock.unlock();
                        toAccount.lock.unlock();
                    }
    
                } else {
                     System.out.println("Unable to begin transaction");
                }
    
            }
    
            boolean impendingTransaction(Account fromAccount, Account toAccount) {
    
                Boolean fromAccountLock = false;
                Boolean toAccountLock = false;
    
                try {
                    fromAccountLock = fromAccount.lock.tryLock();
                    toAccountLock = toAccount.lock.tryLock();
                } finally {
                    if (!(fromAccountLock && toAccountLock)) {
                        if (fromAccountLock) {
                            fromAccount.lock.unlock();
                        }
                        if (toAccountLock) {
                            toAccount.lock.unlock();
                        }
                    }
                }
    
                return fromAccountLock && toAccountLock;
            }
    
        }
    
        private class WrapperTransfer implements Runnable {
            private Account fromAccount;
            private Account toAccount;
            private double amount;
    
            public WrapperTransfer(Account fromAccount,Account toAccount,double amount){
                this.fromAccount = fromAccount;
                this.toAccount = toAccount; 
                this.amount = amount;
            }
    
            public void run(){
                Random random = new Random();
                try {
                    int n = random.nextInt(1000);
                    int TIME = 1000 + n; // 1 second + random delay to prevent livelock
                    Thread.sleep(TIME);
                } catch (InterruptedException e) {}
                new Transfer().transfer(fromAccount, toAccount, amount);
            }
    
        }
    
        public void initiateDeadLockTransfer() {
            Account from = new Account(1, 1000);
            Account to = new Account(2, 300);       
            new Thread(new WrapperTransfer(from,to,200)).start();
            new Thread(new WrapperTransfer(to,from,300)).start();
        }
    
        public static void main(String[] args) {
            new FixDeadLock1().initiateDeadLockTransfer();
        }
    
    }
    

    原子学不会抛出中断异常。正确!但我敢打赌
    账户。当OP着手处理时,贷方/借方
    会抛出一些东西。这在处理两个以上的账户时会起作用,对吗?我可能不太理解这个概念,但在两个账户余额相同的情况下呢?据我所知,它们不会被交换,因此死锁仍然存在。@Piotr:不,在这种情况下,您可以根据它们特有的内容(例如帐号或它们在数据库中的主键等)对帐户进行排序。只要在所有参与者中都是稳定的顺序,实际的顺序就无关紧要了(也就是说,正如你所建议的,没有重复)。@WillHartung谢谢Will,这听起来很有说服力!