天天看点

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

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)死锁的四个必要条件?

  • 互斥资源
  • 循环等待
  • 请求与保持
  • 不可剥夺
  • 对于“占用且等待”这个条件,我们可以一次性申请所有的资源,这样就不存在等待了。
  • 对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了。
  • 对于“循环等待”这个条件,可以靠按序申请资源来预防。所谓按序申请,是指资源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环了。
  • 一次性申请所有资源
  • 增加一个账本管理员,然后只允许账本管理员从文件架上拿账本,当张三要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 是可以轻松解决这个问题的
  • 对资源进行排序,然后按序申请资源
  • 锁排序法
  • 不局限于当下,可以换个思路,向现实世界要答案,利用现实世界的模型来构思解决方案