俗话说,人多好办事!在程序里也是这样,如果是同一个应用程序需要并行处理多件任务,那就可以创建多条线程。但是人多了,往往会出现冲突,使得这个工作无法再进行下去了,(三个和尚没水喝啊!)这就是“死锁”。
死锁,举个形象的例子,就像3(A、B、C)个人在玩3个球(1、2、3),规则很简单:每个人都必须先拿到自己左手边的球,才能拿自己右边的球,两手都有球之后,才能把球都放下。
这个游戏看起来似乎可以永远进行下去,但是若干局之后,如果三个人刚好都只拿到左手边的球,都等着那右手边的球,但是因为谁都不能放手,那么这三个人(线程)都将陷入无尽的等待中了,这就是传说中的“死锁”。
下面就用Java举例,例子中已经创建了3个boolean型的静态变量ball1、ball2、ball3(初始值为FALSE),TRUE代表球被拿起,FALSE代表球仍放在地上,接下来就是3个线程类:
Class PlayerA extends Thread //A的线程
{
Public void run()
{
While(TRUE) //无限循环
{
While(ball3==TRUE) {} //如果ball3已被拿起,则进入等待
ball3=TRUE; //当ball3被放下后,立刻拿起
While(ball1==TRUE) {} //如果ball1已被拿起,则进入等待
ball1=TRUE; //拿起ball1
System.out.println(“A已经拿到两球!”)//为了方便观察死锁现象
ball1=FALSE;ball3=FALSE;//然后放下两球
}
}
}
Class PlayerB extends Thread //B的线程
{
Public void run()
{
While(TRUE) //无限循环
{
While(ball1==TRUE) {} //如果ball1已被拿起,则进入等待
ball1=TRUE; //当ball1被放下后,立刻拿起
While(ball2==TRUE) {} //如果ball2已被拿起,则进入等待
ball2=TRUE; //拿起ball2
System.out.println(“B已经拿到两球!”)//为了方便观察死锁现象
ball2=FALSE;ball1=FALSE;//然后放下两球
}
}
}
Class PlayerC extends Thread //C的线程
{
Public void run()
{
While(TRUE) //无限循环
{
While(ball2==TRUE) {} //如果ball2已被拿起,则进入等待
ball2=TRUE; //当ball2被放下后,立刻拿起
While(ball3==TRUE) {} //如果ball3已被拿起,则进入等待
ball3=TRUE; //拿起ball1
System.out.println(“C已经拿到两球!”)//为了方便观察死锁现象
ball3=FALSE;ball2=FALSE;//然后放下两球
}
}
}
Main()函数略,运行这个程序,你会看到有若干行打印信息后,就不再有输出,那么就说明它“死锁”了。
那么我们如何来消除“死锁”呢?首先,让我们来看看产生“死锁”的必要条件:
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=FLASE;
break Outer;
}
}
ball1=TRUE; //拿起ball1
System.out.println(“A已经拿到两球!”)//为了方便观察死锁现象
ball1=FALSE;ball3=FALSE;//然后放下两球
}
其它两个线程也是如此,即可打破占有等待;
非剥夺
其实打破非剥夺,只要给线程制定一个优先级即可。比如例子中,我们设优先级从高到低为A、B、C,既当A需要ball3,而C正占有它,但是A的优先级比C高,那么C必须马上释放ball3。同理,A对B、B对C也是如此。代码修改如下:
Class PlayerA extends Thread //A的线程,优先级最高
{
Public void run()
{
While(TRUE) //无限循环
{
if(ball3==TRUE) //如果ball3已被C拿起
ball3=FALSE; //则“强迫”C放下ball3
ball3=TRUE; //当ball3被放下后,立刻拿起
if(ball1==TRUE) //如果ball1已被B拿起
ball1=FALSE; //则“强迫”B放下ball1
ball1=TRUE; //拿起ball1
System.out.println(“A已经拿到两球!”);
ball1=FALSE;ball3=FALSE;//然后放下两球
}
}
}
Class PlayerB extends Thread //B的线程,优先级第二
{
Public void run()
{
While(TRUE) //无限循环
{
While(ball1==TRUE) {} //如果ball1已被A拿起,则进入等待
ball1=TRUE; //当ball1被放下后,立刻拿起
if(ball2==TRUE) //如果ball1已被C拿起
ball2=FALSE; //则“强迫”C放下ball2
ball2=TRUE; //拿起ball2
System.out.println(“B已经拿到两球!”)//为了方便观察死锁现象
ball2=FALSE;ball1=FALSE;//然后放下两球
}
}
}
Class PlayerC extends Thread //C的线程,优先级最低
{
Public void run()
{
While(TRUE) //无限循环
{
While(ball2==TRUE) {} //如果ball2已被拿起,则进入等待
ball2=TRUE; //当ball2被放下后,立刻拿起
While(ball3==TRUE) {} //如果ball3已被拿起,则进入等待
ball3=TRUE; //拿起ball1
System.out.println(“C已经拿到两球!”)//为了方便观察死锁现象
ball3=FALSE;ball2=FALSE;//然后放下两球
}
}
}
通过这样的修改我们就能打破“非剥夺”(唉~和这个社会一样,可怜的小C啊!)。
最后的循环等待的解决方法其实和占有等待是一样的,都是等待一段时间后释放资源。好了,希望通过这个例子能让读者对“死锁”有一定的认识。
- 大小: 5.9 KB
分享到:
相关推荐
### Java多线程死锁预防机制研究 #### 摘要 随着计算机技术的发展和软件系统日益复杂的背景下,多线程编程技术得到了广泛的应用。多线程能够显著提高程序的执行效率,但也随之带来了诸如死锁等问题。死锁是指两个或...
多线程死锁的危险 - 临界区函数的不当使用 在多线程编程中,临界区函数的使用是非常重要的。然而,许多开发者却忽视了临界区函数的正确使用方法,导致了多线程死锁的出现。本文将讲述一个关于在类的构造函数和析构...
明白死锁产生的原因,在程序中演示死锁产生并从而实现多线程陈旭解决死锁(deadlock)这一类问题。
DllMain 和多线程死锁 DllMain 是可选择的 DLL 入口指针,当进程和线程启动和终止时被系统调用,分别进行创建资源和释放资源等操作。在 DllMain 中创建线程或结束线程时,都特别要注意一个规则,那就是 DllMain 的...
DllMain和多线程死锁问题 DllMain是Windows操作系统中的动态链接库(DLL)入口点,当进程和线程启动和终止时被系统调用,分别进行创建资源和释放资源等操作。在DllMain中创建线程或终止线程时,如果违背了DllMain的...
### 多线程死锁、活锁与竞争锁问题总结 #### 一、多线程基础知识简介 在探讨多线程编程中常见的问题之前,我们首先简要回顾一下多线程的基本概念。多线程是一种允许多个线程在同一进程中并发执行的技术。每个线程...
一个简单有效的即时检测线程死锁的方法(附c++源代码) 原文链接:https://blog.csdn.net/liaozhilong88/article/details/80354414 原链接是windows下实现,这份代码以pthread简单实现跨平台 感谢原博主分享
实测有效的一个c++检测线程死锁的解决方法(实现和测试代码) 原创实测有效的一个c++检测线程死锁的解决方法,已应用于项目,实测有效 原创文章地址:https://blog.csdn.net/liaozhilong88/article/details/80354414...
在Java多线程编程中,死锁是一个非常重要的概念,也是开发者需要密切关注和避免的问题。死锁是指两个或多个线程互相等待对方释放资源,从而导致它们都无法继续执行的状态。这种情况通常发生在并发环境中,当线程间的...
Java Swing多线程死锁问题解析 Java Swing多线程死锁问题解析是Java开发者经常遇到的问题之一。在基于Java Swing进行图形界面开发时,经常会遇到多线程问题。如果在图形界面的同一个线程中进行查询和运算工作,则会...
根据提供的文件内容,本篇报告主要涉及在Linux环境下通过C语言编程实现线程死锁的实验。报告中包含了一段实验代码,以及实验的基本框架和实验过程。下面详细解释报告中的知识点: 1. Linux系统与C语言编程: Linux...
在Linux系统中,多线程编程是常见的并发执行方式,然而如果不小心,它可能会引发一些问题,特别是死锁。死锁是指两个或多个线程在等待对方释放资源,导致它们都无法继续执行的状态。在这个特定的案例中,我们看到12...
在本示例中,我们将深入探讨Java多线程死锁的概念,并通过一个具体的代码示例来理解其工作原理和避免策略。 首先,我们来看标题中的关键点——“Java多线程死锁示例”。死锁是多线程编程中的一种异常状态,通常发生...
线程死锁是多线程编程中一个严重的问题,它发生在两个或多个线程相互等待对方释放资源,导致它们都无法继续执行。这个概念在计算机科学中尤为重要,因为并发执行是提高系统性能的关键手段,但如果不妥善处理,死锁...
在处理多线程死锁时,一个重要的原则是避免循环等待。这意味着确保所有线程按相同顺序获取资源,以防止形成环路。此外,可以使用条件变量、信号量等高级同步机制来协调线程间的操作,或者使用死锁预防和死锁避免算法...
【多线程死锁的产生以及如何避免死锁方法详解】 多线程死锁是并发编程中一个重要的问题,它发生在多个线程之间,当它们因竞争资源而陷入一种互相等待的状态,没有外部干预,都无法继续执行。了解死锁的产生原因及...
SQLite3.30.1是SQLite数据库引擎的一个特定版本,专为易语言用户设计,旨在提供一个高效且可靠的多线程数据库解决方案,特别是在处理可能引发死锁问题的并发操作时。SQLite是一个开源、轻量级的嵌入式数据库,常用于...
Java多线程死锁是并发编程中一个严重的问题,它发生在两个或更多个线程相互等待对方持有的资源而无法继续执行的情况。理解死锁的概念及其产生原因对于避免和解决这类问题至关重要。 首先,死锁产生的主要原因包括:...