因为不当的执行顺序致使的死锁

为了保证线程的安全,咱们引入了加锁机制,可是若是不加限制的使用加锁,就有可能会致使顺序死锁(Lock-Ordering Deadlock)。上篇文章咱们也提到了在线程词中由于资源的不足而致使的资源死锁(Resource Deadlock)。java

本文将会讨论一下顺序死锁的问题。git

咱们来讨论一个常常存在的帐户转帐的问题。帐户A要转帐给帐户B。为了保证在转帐的过程当中A和B不被其余的线程意外的操做,咱们须要给A和B加锁,而后再进行转帐操做, 咱们看下转帐的代码:github

public void transferMoneyDeadLock(Account from,Account to, int amount) throws InsufficientAmountException {
        synchronized (from){
            synchronized (to){
                transfer(from,to,amount);
            }
        }
    }

    private void transfer(Account from,Account to, int amount) throws InsufficientAmountException {
        if(from.getBalance() < amount){
            throw new InsufficientAmountException();
        }else{
            from.debit(amount);
            to.credit(amount);
        }
    }
复制代码

看起来上面的程序好像没有问题,由于咱们给from和to都加了锁,程序应该能够很完美的按照咱们的要求来执行。安全

那若是咱们考虑下面的一个场景:ide

A:transferMoneyDeadLock(accountA, accountB, 20)
B:transferMoneyDeadLock(accountB, accountA, 10复制代码

若是A和B同时执行,则可能会产生A得到了accountA的锁,而B得到了accountB的锁。从然后面的代码没法继续执行,从而致使了死锁。spa

对于这样的状况,咱们有没有什么好办法来处理呢?线程

加入无论参数怎么传递,咱们都先lock accountA再lock accountB是否是就不会出现死锁的问题了呢?code

咱们看下代码实现:ci

private void transfer(Account from,Account to, int amount) throws InsufficientAmountException {
        if(from.getBalance() < amount){
            throw new InsufficientAmountException();
        }else{
            from.debit(amount);
            to.credit(amount);
        }
    }

    public void transferMoney(Account from,Account to, int amount) throws InsufficientAmountException {

       int fromHash= System.identityHashCode(from);
       int toHash = System.identityHashCode(to);

       if(fromHash < toHash){
           synchronized (from){
               synchronized (to){
                   transfer(from,to, amount);
               }
           }
       }else if(fromHash < toHash){
            synchronized (to){
                synchronized (from){
                    transfer(from,to, amount);
                }
            }
        }else{
           synchronized (lock){
           synchronized (from) {
               synchronized (to) {
                   transfer(from, to, amount);
               }
             }
           }
       }
    }
复制代码

上面的例子中,咱们使用了System.identityHashCode来得到两个帐号的hash值,经过比较hash值的大小来选定lock的顺序。资源

若是两个帐号的hash值刚好相等的状况下,咱们引入了一个新的外部lock,从而保证同一时间只有一个线程可以运行内部的方法,从而保证了任务的执行而不产生死锁。

本文的例子能够参考github.com/ddean2009/l…

更多内容请访问 flydean的博客

相关文章
相关标签/搜索