第一部分:并发理论基础05->死锁了怎么办

1.3个人互相转账

3个人互相转账,串行

2.账本

  1. 文件架上有转出账本+转入账本,同时拿走
  2. 文件架上只有转出账本,柜员就先把文件架上的转出账本拿到手里,同时等着另一个账本送回来。
  3. 文件架上一个账本都没有,等两个账本都回来。

3.明细


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

4.如何预防死锁

出现死锁,以下4个条件

  1. 互斥,共享资源x和y只能被一个线程占用
  2. 占有且等待,线程T1已经取得共享资源x,在等待共享资源y的时候,不释放共享资源x
  3. 不可抢占,其他线程不能强行占用T1占有的资源
  4. 循环等待,线程T1等待T2占有的资源,T2等待T1占有的资源。循环等待

破坏其中任意一个条件,就可以避免死锁发生

  1. 占有且等待,一次性申请所有资源,就不存在等待了
  2. 不可抢占条件,占用资源线程进一步申请其他资源时,申请不到,可以主动释放它占有的资源,不可抢占条件就破坏了
  3. 循环等待条件,按序申请资源。

5.预防死锁的案例

  1. 破坏占用且等待条件
    一次性申请所有资源,就同时拿到转入账本和转出账本的概念

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)
    }
  } 
}
  1. 破坏不可抢占条件
    主动释放它占用的资源,synchronized实现不了,但是Lock可以实现

  2. 破坏循环等待条件
    需要对资源进行排序,按序申请资源。


class Account {
  private int id;
  private int balance;
  // 转账
  void transfer(Account target, int amt){
    Account left = this        ①
    Account right = target;    ②
    if (this.id > target.id) { ③
      left = target;           ④
      right = this;            ⑤
    }                          ⑥
    // 锁定序号小的账户
    synchronized(left){
      // 锁定序号大的账户
      synchronized(right){ 
        if (this.balance > amt){
          this.balance -= amt;
          target.balance += amt;
        }
      }
    }
  } 
}

6.总结

细粒度锁,锁多个资源,注意死锁问题
破坏死锁条件中的任意一个,就不会出现死锁

posted @ 2021-06-30 16:21  SpecialSpeculator  阅读(33)  评论(0编辑  收藏  举报