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

    关于多线程死锁,以前对这个概念总是很朦胧,不知道具体该怎么理解。

    前不久从网上看到一篇文章,感觉写的很透彻,很形象,现在摘录下来以备日后省查,希望也能对其他人起到帮助。

    俗话说的好,人多好办事!在程序当中也是这样,如果在同一个应用程序中需要并行处理多件任务,那就可以创建

    多条线程。但是人多了,往往也会出现冲突,使得这个工作无法进行下去了,(三个和尚没水喝啊),这就是死锁。

    举个形象的例子,就像三个人(A,B,C)在玩三个球(1,2,3),规则很简单,每个人都必须先拿到自己左手边的球,

    才能拿自己右边的球,两手都有球之后,才能把球放下。如下图。

    这个游戏看起来似乎可以永远进行下去,但是若干局之后,如果三个人刚好都只拿到自己左手边的球,都等着拿右手边的球,

    但是因为谁都不能放手,那么这三个人(线程)都将陷入无尽的等待中了,这就是传说中的死锁。

    下面就用JAVA来举例,例子中已经创建了三个boolean型的静态变量ball1、ball2、ball3(初始值为false),TURE代表球

    被拿起,FALSE代表球仍放在地上,接下来就是三个线程类:

    public class test {
    
        static  class ThreadA extends Thread //A的线程
        {
    
            @Override
            public void run() {
                // TODO Auto-generated method stub
                //super.run();
                while(true)//无限循环
                {
                    while(ball3==true)//如果ball3已被拿起,贼进入等待。
                    {}
                    ball3=true;//当ball3被放下后,立刻拿起
                    while(ball1==true)//如果ball1已被拿起,则进入等待。
                    {}
                    ball1=true;//拿起ball1.
                    System.out.println("A已经拿到两球");//输出结果,方便观察死锁状态
                    ball1=ball3=false;//放下两球
                }
            }
            
        }
        static class ThreadB extends Thread //B的线程
        {
    
            @Override
            public void run() {
                // TODO Auto-generated method stub
                //super.run();
                while(true)//无限循环
                {
                    while(ball1==true)//如果ball1已被拿起,贼进入等待。
                    {}
                    ball1=true;//当ball1被放下后,立刻拿起
                    while(ball2==true)//如果ball2已被拿起,则进入等待。
                    {}
                    ball2=true;//拿起ball1.
                    System.out.println("B已经拿到两球");//输出结果,方便观察死锁状态
                    ball1=ball2=false;//放下两球
                }
            }
            
        }
        static class ThreadC extends Thread //C的线程
        {
    
            @Override
            public void run() {
                // TODO Auto-generated method stub
                //super.run();
                while(true)//无限循环
                {
                    while(ball2==true)//如果ball2已被拿起,贼进入等待。
                    {}
                    ball2=true;//当ball2被放下后,立刻拿起
                    while(ball3==true)//如果ball3已被拿起,则进入等待。
                    {}
                    ball3=true;//拿起ball3.
                    System.out.println("C已经拿到两球");//输出结果,方便观察死锁状态
                    ball2=ball3=false;//放下两球
                }
            }
            
        }
        /**
         * @param args
         */
    private static boolean ball1,ball2,ball3=false;
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            ThreadA A=new ThreadA();
            ThreadB B=new ThreadB();
            ThreadC C=new ThreadC();
            A.start();
            B.start();
            C.start();
        }
    
    }

    运行这个程序,你会看到有若干行打印信息后,就不再有输出,那么就说明它“死锁”了。

    那么我们如何来消除“死锁”呢?首先,让我们来看看产生“死锁”的必要条件:

    1. 互斥,就是说多个线程不能同时使用同一资源,比如,当线程A使用该资源时,B线程只能等待A释放后才能使用。

    2. 占有等待,就是某线程必须同时拥有N个资源才能完成任务,否则它将占用已经拥有的资源直到拥有他所需的所有资源为止,就好像游戏中,必须两个球都拿到了,才能释放;

    3.非剥夺,就是说所有线程的优先级都相同,不能在别的线程没有释放资源的情况下,夺走其已占

    有的资源;

    4.循环等待,就是没有资源满足的线程无限期的等待。

    到了这,有的读者已经明白了,只要打破这几个必要条件,就能打破“死锁”!那么先来看看互斥:

    要打破这个条件,就是要让多个线程能共享资源,就相当于A和B能同时举起ball1一样,当然在

    这个例子里我们可以这样修改规则,但是在其他程序中就不一定能了,比如说一个“读”线程,一

    个“写”线程,他们都能操作同一文件。这种情况下,我们就不能“又读又写”文件,否则有可能会读

    到脏数据!因此我们很少从这方面考虑。

    占有等待:

    打破占有等待,只要当检测到自己所需的资源仍被别的线程占用,即释放自己已占有的资源(毫不利己,专门利人,呵呵~),或者在经过一段时间的等待后,还未得到所需资源,才释放,这就能打破占有等待。我们可以把While(true)中的代码改一下(以A为例):

    Outer:While(true)

    {

    int i=0;

    while(ball3==true){}//如果ball3已被拿起,则进入等待

    ball3=true;//当ball3被放下后,立刻拿起。

    while(ball1==true)

    {

    i++;

    if(i==1000)//当计数达到1000后还未得到ball1,则放下ball3,并重新开始。

    {

    ball3=false;

    break Outer;

    }

    ball1=true;//拿起ball1

    System.out.println(“A已经拿到两球!”)//为了方便观察死锁现象。

    ball1=ball3=false;//然后放下两球

    }

    }

    其他两个线程也是如此,即可打破占有等待;

    非剥夺:

    打破非剥夺,只要给线程指定一个优先级即可。比如例子中,我们设优先级从高到低为A、B、C

    ,即当A需要ball3,而C正占有它,但是A的优先级比C高,那么C必须马上释放ball3.同理,A对B、B对C也是如此。代码修改如下:

    public class test {
    
        static  class ThreadA extends Thread //A的线程,优先级最高。
        {
    
            @Override
            public void run() {
                // TODO Auto-generated method stub
                //super.run();
                while(true)//无限循环
                {
                    while(ball3==true)//如果ball3已被C拿起,贼进入等待。
                    {
                ball3=false;//强迫C放下ball3
              } ball3
    =true;//当ball3被放下后,立刻拿起 while(ball1==true)//如果ball1已被B拿起,则进入等待。 {ball1=false;}//强迫B放下ball1 ball1=true;//拿起ball1. System.out.println("A已经拿到两球");//输出结果,方便观察死锁状态 ball1=ball3=false;//放下两球 } } } static class ThreadB extends Thread //B的线程,优先级第二 { @Override public void run() { // TODO Auto-generated method stub //super.run(); while(true)//无限循环 { while(ball1==true)//如果ball1已被A拿起,贼进入等待。 {} ball1=true;//当ball1被放下后,立刻拿起 while(ball2==true)//如果ball2已被C拿起,则进入等待。 {ball2=false;}//强迫C放下ball2 ball2=true;//拿起ball1. System.out.println("B已经拿到两球");//输出结果,方便观察死锁状态 ball1=ball2=false;//放下两球 } } } static class ThreadC extends Thread //C的线程,优先级最低 { @Override public void run() { // TODO Auto-generated method stub //super.run(); while(true)//无限循环 { while(ball2==true)//如果ball2已被拿起,贼进入等待。 {} ball2=true;//当ball2被放下后,立刻拿起 while(ball3==true)//如果ball3已被拿起,则进入等待。 {} ball3=true;//拿起ball3. System.out.println("C已经拿到两球");//输出结果,方便观察死锁状态 ball2=ball3=false;//放下两球 } } } /** * @param args */ private static boolean ball1,ball2,ball3=false; public static void main(String[] args) { // TODO Auto-generated method stub ThreadA A=new ThreadA(); ThreadB B=new ThreadB(); ThreadC C=new ThreadC(); A.start(); B.start(); C.start(); } }

    通过这样的修改我们就能打破“非剥夺”(唉~和这个社会一样,可怜的小C啊!)。

    最后的循环等待的解决方法其实和占有等待是一样的,都是等待一段时间后释放资源,好了,希望

    能通过这个例子让读者对“死锁”有一定的认识。

  • 相关阅读:
    grep 命令操作
    vi & vim复制,粘贴,剪切文本
    β版本第四次冲刺
    β版本第三次冲刺
    β版本第二次冲刺
    β版本第一次冲刺
    华为软件开发云,个人评测及体会
    事后诸葛亮
    【Alpha】团队课程展示
    团队Alpha博客链接目录
  • 原文地址:https://www.cnblogs.com/onsuccessway/p/3612151.html
Copyright © 2011-2022 走看看