黑马程序员技术交流社区

标题: (主推) 死锁总结 [打印本页]

作者: 朱晓杰    时间: 2013-5-7 21:33
标题: (主推) 死锁总结
本帖最后由 朱晓杰 于 2013-5-7 23:26 编辑

一、理解死锁

    经典的"哲学家进餐"的问题很好的描述了死锁状况。

    5个哲学家去吃中餐,坐在一张圆桌旁。他们有5根筷子(而不是5双),并且每两个人之间放一根筷子,

哲学家们时而思考,时而进餐。每个人都需一双筷子才能吃到东西,并在吃完后将筷子放到原处继续思考。

如果每个人都立即抓住自己左边的筷子,然后等待右边的筷子空出来,但同时又不放下已经拿到的筷子。

这种情况将产生死锁:每个人都拥有其他人需要的资源,同时又等待其他人已经拥有的资源,并且每个人

在获得所有需要的资源之前都不会放弃已经拥有的资源。

     死锁(DeadLock),是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。
                 
    产生死锁的四个必要条件是:

     1:互斥条件:资源每次只能被一个线程使用。如前面的“线程同步代码段”。

     2:请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。

     3:不剥夺条件:进程已获得的资源,在未使用完之前,无法强行剥夺。

     4:循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

     这四个条件是死锁的必要条件,只要发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

二、死锁示例

1.最简单的死锁形式

        在线程A持有锁L并想获得锁M的同时,线程B持有锁M并尝试获得锁L那么这两个线程将永远的等待下去
  1. public class DeadLockDemo {
  2.                 public static void main(String[] args) {
  3.                         DeadLock d = new DeadLock();
  4.                         Thread a = new Thread(d);
  5.                         Thread b = new Thread(d);
  6.                         a.start();
  7.                         b.start();
  8.                 }
  9.         }

  10.         class DeadLock implements Runnable{
  11.                 private boolean flag = true;//控制线程的操作交替进行
  12.                 private final Object l = new Object();
  13.                 private final Object m = new Object();//定义两个静态锁
  14.         
  15.                 public void run(){
  16.                         while(true){
  17.                                 if(flag){
  18.                                         synchronized(l){//①
  19.                                                 System.out.println("...if l...");
  20.                                                 synchronized(m){//②
  21.                                                         System.out.println("...if m...");
  22.                                                 }
  23.                                         }
  24.                                         flag = false;
  25.                                 }else{
  26.                                         synchronized(m){//③
  27.                                                 System.out.println("...else m...");
  28.                                                 synchronized(l){//④
  29.                                                         System.out.println("...else l...");
  30.                                                 }
  31.                                         }
  32.                                         flag = true;
  33.                                 }
  34.                         }
  35.                 }
  36.         }
复制代码
发生死锁的原因:两个线程试图以不同的顺序来获得相同的锁。
分析:


线程a执行到①,锁定了M,此时线程b抢占了CPU,获得了执行权,锁定了L,而线程a对M的锁定还没有解除,导致线程b无法运行下去,同样由于线程b锁定了L,线程a也无法运行。

2.银行转账死锁示例
  1. /*
  2. * 需求:简单的银行转账,它将资金从一个账户转到另一个账户
  3. * 在开始转账之前,需要获得两个Account的锁,以确保以原子的方式更新账户中的余额,且不能破坏不可变的条件,如账户的余额不能为负数
  4. */

  5. /*账户类*/
  6. class Account{
  7.         private String accountName;//账号
  8.         private int balance;//资金总额
  9.         public Account(String accountName,int balance){
  10.                 this.accountName = accountName;
  11.                 this.balance = balance;
  12.         }
  13.         /**/
  14.         public String getAccountName() {//获取账号
  15.                 return accountName;
  16.         }
  17.         public int getBalance() {//获取账号余额
  18.                 return balance;
  19.         }
  20.         
  21.         public void debit(int amount){//更新转出方余额
  22.                 this.balance -= amount;
  23.         }
  24.         
  25.         public void credbit(int amount){//更新转入方余额
  26.                 this.balance += amount;
  27.         }
  28. }

  29. class TransferAccount implements Runnable{
  30.         public Account fromAccount;//转出账户
  31.         public Account toAccount;//转入账户
  32.         public int amount;//转出金额
  33.         public TransferAccount(Account fromAccount,Account toAccount,int amount){
  34.                 this.fromAccount = fromAccount;
  35.                 this.toAccount = toAccount;
  36.                 this.amount = amount;
  37.         }
  38.         public void run(){
  39.                 while(true){
  40.                         synchronized(fromAccount){
  41.                                 synchronized(toAccount){
  42.                                         if(fromAccount.getBalance() <= 0){//转账进行的条件:判断转出账户的余额是否大于0
  43.                                                 System.out.println(fromAccount.getAccountName() + "账户余额不足,无法进行转账");
  44.                                                 return;
  45.                                         }else{
  46.                                                 fromAccount.debit(amount);//更新转出账户的余额:-
  47.                                                 toAccount.credbit(amount);//更新转入账户的余额:+
  48.                                         }
  49.                                 }
  50.                         }
  51.                         System.out.println(fromAccount.getAccountName() + "......" + fromAccount.getBalance());//打印转出账户的余额
  52.                         System.out.println(toAccount.getAccountName() + "---" + toAccount.getBalance());//打印转入账户的余额
  53.                 }
  54.         }
  55. }

  56. public class BankTransfer {
  57.         public static void main(String[] args) {
  58.                 Account fromAccount = new Account("张三",100000);
  59.                 Account toAccount = new Account("李四",200000);
  60.                
  61.                 Thread a = new Thread(new TransferAccount(fromAccount,toAccount,1));
  62.                 Thread b = new Thread(new TransferAccount(toAccount,fromAccount,2));
  63.                
  64.                 a.start();
  65.                 b.start();
  66.         }
  67. }
复制代码
发生死锁的原因:两个线程试图以不同的顺序来获得相同的锁
分析:所有的线程并不是按照相同的顺序来获取锁,锁的顺序取决于传递的参数顺序,而这些参数顺序又取决于外部输入,如果线程a和线程b同时转账,线程a从张三向李四转账,线程b从李四向张三转账,那么就会发生死锁:
         a:new TransferAccount(fromAccount,toAccount,1)      
         b:new TransferAccount(toAccount,fromAccount,2)
         如果执行顺序不恰当,那么a可能获得fromAccount的锁并且等待toAccount的锁,然而此时b拥有toAccount的锁,并正在等待fromAccount的锁。            

作者: 朱晓杰    时间: 2013-5-7 23:28
抢楼!今天困了,明天如果还有时间,再来一个Demo!
作者: 朱晓杰    时间: 2013-5-8 09:15
谢版主赐婚!
作者: 张林敏    时间: 2013-5-8 12:43
不错,顶一个...




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2