《java并发编程实战》 第十章 活跃性危险与如何避免

本文详细介绍了Java并发编程中的活跃性危险,如死锁、饥饿和活锁,以及如何避免这些问题。针对死锁,讨论了经典哲学家进餐问题和动态顺序死锁,提出了按固定顺序获取锁的解决方案。同时,强调了开放调用在避免死锁中的重要性,并介绍了使用支持定时的锁和线程转储来诊断和避免死锁的方法。此外,还讨论了资源死锁以及饥饿和活锁的现象及解决策略。
摘要由CSDN通过智能技术生成

第十章 活跃性危险与如何避免

活跃性危险在第一章介绍,包括死锁、饥饿、活锁。

死锁

  经典的“哲学家进餐”问题: 5个哲学家去吃中餐,坐在一张圆桌旁,他们有五根筷子(不是五双),并且每两个人中间放一根筷子。哲学家们时而思考,时而进餐。每个人都需要一双筷子才能吃到东西,并且在吃完后将筷子放回原处继续思考。 如果每个人都立即抓住自己左边的筷子,然后等待自己右边的筷子空出来,但同时又不放下已经拿到的筷子。——产生死锁
  也就是 每个人都拥有其他人需要的资源,同时又等待其他人已经拥有的资源,并且每个人在获得所有需要的资源之前都不会放弃已经拥有的资源。 (正确的做法是尝试获得两根邻近的筷子,如果其中一根正在被另一个哲学家使用,那么他应该放弃自己得到的那根,等待一段时间后再尝试。)
  当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么它们将永远被阻塞。 在线程A持有锁L并想获得锁M的同时,线程B持有锁M并尝试获得锁L,那么这两个线程将永远地等待下去。这种情况就是最简单的死锁形式(或者称为“抱死[Deadly Embrace]“) 。
在数据库系统的设计中考虑了监测死锁以及从死锁中恢复,在执行一个事务时可能需要获得多个锁,并一直持有这些锁直到事务提交。因此两个事务之间很可能发生死锁,但事实上这种情况并不多见。数据库服务器不会让这种情况发生,当它检测到一组事务发生死锁时,将选择一个牺牲者并放弃这个事务。作为牺牲者的事务会释放它所持有的资源,从而使其他事务继续进行。
  而java解决死锁问题方面没有数据库服务那么强大,发生死锁时可能造成应用程序完全停止,或者某个特定的子系统停止,或者性能降低,恢复应用程序唯一方式就是中止并重启。解决死锁的思路,所有线程以固定的顺序来获得锁,在程序中就不会出现锁顺序死锁问题。

顺序死锁

在这里插入图片描述
原因:发生死锁的原因是两个线程A和B试图以不同的顺序来获得相同的锁。如果按照相同的顺序来请求锁,就不会出现循环的加锁依赖性,因此也就不会发生死锁
解决方法:按照相同的顺序来请求锁

//容易发生死锁
public class LeftRightDeadlock {
   
     private final Object left = new Object() ;
     private final Object right = new Object() ;
     public void leftRight(){
   
           synchronized (left) {
   
                 synchronized (right) {
   
                       //doSomething();
                 }
           }
     }
     public void rightLeft(){
   
           synchronized (right) {
   
                 synchronized (left) {
   
                       //doSomethingElse();
                 }
           }
     }
}

动态的顺序死锁

  动态的顺序死锁不同于LeftRightDeadlock 一眼就能看出,很多看似无害的代码,当运行后才能发现出现死锁。动态的顺序死锁原因在于并不清楚是否在锁顺序上有足够的控制权来避免死锁的发生。
  如果两个线程同时调用transferMoney,其中一个线程从X往Y转账,另一个线程从Y往X转账。很有可能A获得myAccount的锁等待yourAccount的锁,然而B此时持有yourAccount锁等待myAccount的锁。
    transferMoney(myAccount,yourAccount,10);
    transferMoney(yourAccount,myAccount,20);

public class DynamicDeadlock {
   
   /**
    * 转账操作(会发生死锁)
    * 两个账户相互给对方转账时,会发生死锁,相当于简单的锁顺序死锁
    * @param fromAccount 转出账户
    * @param toAccount   转入账户
    * @param amount      转账金额
    */
   public void transferMoney(Account fromAccount, Account toAccount, int amount)
           throws InsufficientResourcesException {
   
       synchronized (fromAccount) {
   
           synchronized (toAccount) {
   
               if (fromAccount.getBalance() < 0) {
   
                   throw new InsufficientResourcesException();
               } else {
   
                   fromAccount.debit(amount);
                   toAccount.credit(amount);
               }
           }
       }
   }
   class Account {
   
       
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值