• 一不小心就死锁了,怎么办?


    1)上一节我们用一把大锁锁住银行的转账业务,这样会造成什么样的问题?

    • 所有账户的转账操作都是串行的,性能太差

    • A 转账户 B、账户 C 转账户 D 这两个转账操作现实世界里是可以并行的,但是在这个方案里却被串行化了

    2)那么如何优化可以让我们的账户之间转账和入账能够并行执行呢?

    • 在 transfer() 方法内部,我们首先尝试锁定转出账户 this(先把转出账本拿到手),然后尝试锁定转入账户 target(再把转入账本拿到手),只有当两者都成功时,才执行转账操作。

     
     class Account {
       private int balance;
       // 转账
       void transfer(Account target, int amt){
         // 锁定转出账户
         synchronized(this) {              
           // 锁定转入账户
           synchronized(target) {          
             if (this.balance > amt) {
               this.balance -= amt;
               target.balance += amt;
            }
          }
        }
      }
     }
    • 优化后,账户 A 转账户 B 和账户 C 转账户 D 这两个转账操作就可以并行了

    3)上面我们使用了细粒度锁,那可能会导致什么代价?

    • 死锁

    4)死锁的四个必要条件?

    • 互斥资源

    • 循环等待

    • 请求与保持

    • 不可剥夺

    5)那如何避免死锁呢?

    • 对于“占用且等待”这个条件,我们可以一次性申请所有的资源,这样就不存在等待了。

    • 对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了。

    • 对于“循环等待”这个条件,可以靠按序申请资源来预防。所谓按序申请,是指资源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环了。

    6)5是理论的,那实际的操作是怎样的?

    • 一次性申请所有资源

      • 增加一个账本管理员,然后只允许账本管理员从文件架上拿账本,当张三要AB的时候,管理员看看架子,只有AB都在才会给张三,否则不会给

         
         class Allocator {
           private List<Object> als =
             new ArrayList<>();
           // 一次性申请所有资源
           synchronized boolean apply(
             Object from, Object to){
             if(als.contains(from) ||
                  als.contains(to)){
               return false;  
            } else {
               als.add(from);
               als.add(to);  
            }
             return true;
          }
           // 归还资源
           synchronized void free(
             Object from, Object to){
             als.remove(from);
             als.remove(to);
          }
         }
         
         class Account {
           // actr应该为单例
           private Allocator actr;
           private int balance;
           // 转账
           void transfer(Account target, int amt){
             // 一次性申请转出账户和转入账户,直到成功
             while(!actr.apply(this, target))
               
             try{
               // 锁定转出账户
               synchronized(this){              
                 // 锁定转入账户
                 synchronized(target){          
                   if (this.balance > amt){
                     this.balance -= amt;
                     target.balance += amt;
                  }
                }
              }
            } finally {
               actr.free(this, target)
            }
          }
         }

         

    • 主动释放它占有的资源

      • synchronized 做不到,synchronized 申请资源的时候,如果申请不到,线程直接进入阻塞状态了

      • Java 在语言层次确实没有解决这个问题,不过在 SDK 层面还是解决了的,java.util.concurrent 这个包下面提供的 Lock 是可以轻松解决这个问题的

    • 对资源进行排序,然后按序申请资源

      • 锁排序法

    7)当我们在编程世界里遇到问题时,应该怎样思考?

    • 不局限于当下,可以换个思路,向现实世界要答案,利用现实世界的模型来构思解决方案

  • 相关阅读:
    jQuery中$.proxy()的原理和使用
    JS中各种宽度、高度、位置、距离总结
    js中得call()方法和apply()方法的用法
    google浏览器翻译失败解决方案
    js区分移动设备与PC
    知识积累
    Django
    leetcode 27.Remove Element
    leetcode 28. Implement strStr()
    21. Merge Two Sorted Lists
  • 原文地址:https://www.cnblogs.com/YXBLOGXYY/p/15991742.html
Copyright © 2020-2023  润新知