zoukankan      html  css  js  c++  java
  • 多线程的死锁

    俗话说,人多好办事!在程序里也是这样,如果是同一个应用程序需要并行处理多件任务,那就可以创建多条线程。但是人多了,往往会出现冲突,使得这个工作无法再进行下去了,(三个和尚没水喝啊!)这就是“死锁”。
    死锁,举个形象的例子,就像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

  • 相关阅读:
    算法竞赛入门经典习题2-3 韩信点兵
    ios入门之c语言篇——基本函数——5——素数判断
    ios入门之c语言篇——基本函数——4——数值交换函数
    144. Binary Tree Preorder Traversal
    143. Reorder List
    142. Linked List Cycle II
    139. Word Break
    138. Copy List with Random Pointer
    137. Single Number II
    135. Candy
  • 原文地址:https://www.cnblogs.com/ios9/p/7473415.html
Copyright © 2011-2022 走看看