• 多线程的死锁


    俗话说,人多好办事!在程序里也是这样,如果是同一个应用程序需要并行处理多件任务,那就可以创建多条线程。但是人多了,往往会出现冲突,使得这个工作无法再进行下去了,(三个和尚没水喝啊!)这就是“死锁”。
    死锁,举个形象的例子,就像3(A、B、C)个人在玩3个球(1、2、3),规则很简单:每个人都必须先拿到自己左手边的球,才能拿自己右边的球,两手都有球之后,才能把球都放下。

    image

    这个游戏看起来似乎可以永远进行下去,但是若干局之后,如果三个人刚好都只拿到左手边的球,都等着那右手边的球,但是因为谁都不能放手,那么这三个人(线程)都将陷入无尽的等待中了,这就是传说中的“死锁”。
    下面就用Java举例,例子中已经创建了3个boolean型的静态变量ball1、ball2、ball3(初始值为FALSE),TRUE代表球被拿起,FALSE代表球仍放在地上,接下来就是3个线程类:


      1 
      2 Class PlayerA extends Thread //A的线程 
      3 {
      4 	Public void run() {
      5 		While(TRUE){ //无限循环 
      6 			While(ball3==TRUE) {} //如果ball3已被拿起,则进入等待 
      7 			ball3=TRUE; //当ball3被放下后,立刻拿起 
      8 			While(ball1==TRUE) {} //如果ball1已被拿起,则进入等待 
      9 			ball1=TRUE; //拿起ball1 
     10 			System.out.println(“A已经拿到两球!”)//为了方便观察死锁现象 
     11 			ball1=FALSE;ball3=FALSE;//然后放下两球 
     12 			}
     13       }
     14 }
     15 
     16 
     17 Class PlayerB extends Thread //B的线程 
     18 {
     19 	Public void run() {
     20 		While(TRUE) {//无限循环 
     21 			While(ball1==TRUE) {} //如果ball1已被拿起,则进入等待 
     22 			ball1=TRUE; //当ball1被放下后,立刻拿起 
     23 			While(ball2==TRUE) {} //如果ball2已被拿起,则进入等待 
     24 			ball2=TRUE; //拿起ball2 
     25 			System.out.println(“B已经拿到两球!”)//为了方便观察死锁现象 
     26 			ball2=FALSE;ball1=FALSE;//然后放下两球 
     27 		}
     28 	}
     29 }
     30 
     31 
     32 Class PlayerC extends Thread{  //C的线程 
     33 
     34 	Public void run() {
     35 		While(TRUE){  //无限循环 
     36 				While(ball2==TRUE) {} //如果ball2已被拿起,则进入等待 
     37 				ball2=TRUE; //当ball2被放下后,立刻拿起 
     38 				While(ball3==TRUE) {} //如果ball3已被拿起,则进入等待 
     39 				ball3=TRUE; //拿起ball1 
     40 				System.out.println(“C已经拿到两球!”)//为了方便观察死锁现象 
     41 				ball3=FALSE;ball2=FALSE;//然后放下两球 
     42 		}
     43 	}
     44 }
     45 

    Main()函数略,运行这个程序,你会看到有若干行打印信息后,就不再有输出,那么就说明它“死锁”了。
    那么我们如何来消除“死锁”呢?首先,让我们来看看产生“死锁”的必要条件:
    1. 互斥,就是说多个线程不能同时使用同一资源,比如,当线程A使用该资源时,B线程只能等待A释放后才能使用;
    2. 占有等待,就是某线程必须同时拥有N个资源才能完成任务,否则它将占用已经拥有的资源直到拥有他所需的所有资源为止,就好像游戏中,必须两个球都拿到了,才能释放;
    3. 非剥夺,就是说所有线程的优先级都相同,不能在别的线程没有释放资源的情况下,夺走其已占有的资源;
    4. 循环等待,就是没有资源满足的线程无限期地等待。
    (嘿嘿~操作系统的知识还没有忘啊!)
    有的读者已经明白了,只要打破这这几个必要条件,就能打破“死锁”!那么先来看看互斥:
    要打破这个条件,就是要让多个线程能共享资源,就相当于A和B能同时举起ball1一样,当然在这个例子里我们可以这样修改规则,但是在其它程序中就不一定能了,比如说一个“读”线程,一个“写”线程,它们都能操作同一文件。在这种情况下,我们就不能“又读又写”文件,否则有可能会读到脏数据!因此我们很少从这方面考虑。
    占有等待
    打破占有等待,只要当检测到自己所需的资源仍被别的线程占用,即释放自己已占有的资源(毫不利己,专门利人,呵呵~),或者在经过一段时间的等待后,还未得到所需资源,才释放,这都能打破占有等待。我们可以把While (TRUE)中的代码改一下(以A为例):

      1 Outer:While(TRUE)//做标记 
      2 {
      3    Int i=0;
      4     While(ball3==TRUE) {} //如果ball3已被拿起,则进入等待 
      5      ball3=TRUE; //当ball3被放下后,立刻拿起 
      6     While(ball1==TRUE)
      7     {
      8      i++;
      9 
     10 if(i==1000) //当计数达到1000后还未得到ball1,则放下ball3,并重新开始 
     11 
     12 {
     13 
     14 ball3=FLASE;
     15 
     16 break Outer;
     17 
     18 }
     19 
     20 }
     21 
     22 ball1=TRUE; //拿起ball1 
     23 
     24 System.out.println(“A已经拿到两球!”)//为了方便观察死锁现象 
     25 
     26 ball1=FALSE;ball3=FALSE;//然后放下两球 
     27 
     28 }
     29 

    其它两个线程也是如此,即可打破占有等待;
    非剥夺
    其实打破非剥夺,只要给线程制定一个优先级即可。比如例子中,我们设优先级从高到低为A、B、C,既当A需要ball3,而C正占有它,但是A的优先级比C高,那么C必须马上释放ball3。同理,A对B、B对C也是如此。代码修改如下:


      1 Class PlayerA extends Thread //A的线程,优先级最高 
      2 
      3 {
      4 
      5 Public void run()
      6 
      7 {
      8 
      9 While(TRUE) //无限循环 
     10 
     11 {
     12 
     13 if(ball3==TRUE) //如果ball3已被C拿起 
     14 
     15 ball3=FALSE; //则“强迫”C放下ball3 
     16 
     17 ball3=TRUE; //当ball3被放下后,立刻拿起 
     18 
     19 if(ball1==TRUE) //如果ball1已被B拿起 
     20 
     21 ball1=FALSE; //则“强迫”B放下ball1 
     22 
     23 ball1=TRUE; //拿起ball1 
     24 
     25 System.out.println(“A已经拿到两球!”);
     26 
     27 ball1=FALSE;ball3=FALSE;//然后放下两球 
     28 
     29 }
     30 
     31 }
     32 
     33 }
     34 
     35 
     36 Class PlayerB extends Thread //B的线程,优先级第二 
     37 
     38 {
     39 
     40 Public void run()
     41 
     42 {
     43 
     44 While(TRUE) //无限循环 
     45 
     46 {
     47 
     48 While(ball1==TRUE) {} //如果ball1已被A拿起,则进入等待 
     49 
     50 ball1=TRUE; //当ball1被放下后,立刻拿起 
     51 
     52 if(ball2==TRUE) //如果ball1已被C拿起 
     53 
     54 ball2=FALSE; //则“强迫”C放下ball2 
     55 
     56 ball2=TRUE; //拿起ball2 
     57 
     58 System.out.println(“B已经拿到两球!”)//为了方便观察死锁现象 
     59 
     60 ball2=FALSE;ball1=FALSE;//然后放下两球 
     61 
     62 }
     63 
     64 }
     65 
     66 }
     67 
     68 
     69 Class PlayerC extends Thread //C的线程,优先级最低 
     70 
     71 {
     72 
     73 Public void run()
     74 
     75 {
     76 
     77 While(TRUE) //无限循环 
     78 
     79 {
     80 
     81 While(ball2==TRUE) {} //如果ball2已被拿起,则进入等待 
     82 
     83 ball2=TRUE; //当ball2被放下后,立刻拿起 
     84 
     85 While(ball3==TRUE) {} //如果ball3已被拿起,则进入等待 
     86 
     87 ball3=TRUE; //拿起ball1 
     88 
     89 System.out.println(“C已经拿到两球!”)//为了方便观察死锁现象 
     90 
     91 ball3=FALSE;ball2=FALSE;//然后放下两球 
     92 
     93 }
     94 
     95 }
     96 
     97 }

    通过这样的修改我们就能打破“非剥夺”(唉~和这个社会一样,可怜的小C啊!)。
    最后的循环等待的解决方法其实和占有等待是一样的,都是等待一段时间后释放资源。好了,希望通过这个例子能让读者对“死锁”有一定的认识。.



    本文出自于:coach

  • 相关阅读:
    csu1217: 奇数个的那个数
    小试SAS 判别分析
    bfs poj2965
    STL set常用操作
    csu1002 A+B(III)
    HDOJ 1002 的几种方法
    SQL知识积累
    CSV文件格式介绍
    ASP.net Web Form 知识积累
    C# 位域[flags] 枚举
  • 原文地址:https://www.cnblogs.com/ios9/p/7473415.html
Copyright © 2020-2023  润新知