博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程死锁
阅读量:5221 次
发布时间:2019-06-14

本文共 5928 字,大约阅读时间需要 19 分钟。

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

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

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

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

举个形象的例子,就像三个人(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啊!)。

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

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

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/onsuccessway/p/3612151.html

你可能感兴趣的文章
透明度Opacity多浏览器兼容处理
查看>>
oracle 常用简单命令语句
查看>>
【机器学习_3】常见术语区别
查看>>
Oracle基础 数据库备份和恢复
查看>>
C#编程时应注意的性能处理
查看>>
Java集合--概述
查看>>
1-TwoSum(简单)
查看>>
css box模型content-box 和border-box
查看>>
Fragment
查看>>
比较安全的获取站点更目录
查看>>
读书笔记——乔布斯,做最好的自己,共创式教练
查看>>
ubuontu16.04安装Opencv库引发的find_package()错误信息处理及其简单使用
查看>>
用Linux远程挂载Windows上的共享文件夹.md
查看>>
洛谷 P4317 花神的数论题(组合数)
查看>>
【Python】学习笔记5-利用flask来mock接口
查看>>
vue
查看>>
MySQL存储过程和存储函数
查看>>
【bzoj 2208】[Jsoi2010]连通数(dfs||Tarjan算法+拓扑序+dp)
查看>>
iis 隐藏 banner
查看>>
leetcode[18]4Sum
查看>>