多个线程进行资源竞争,资源的同步不当,便会引发程序的无限等待,即是我们所说的死锁。 本章讲述死锁的原理,以“哲学家就餐问题”进行死锁举例,以及提出避免死锁一些方法。
死锁的概念
两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
死锁条件:
- 1:互斥条件:线程占有资源的互斥访问。
- 2:请求与保持条件:线程至少拥有一个资源,但又需要其他资源,进而阻塞。
- 3:不可剥夺条件:线程已获得的资源,在使用完之前,不可以强行剥夺,只能自己释放。
- 4:循环等待条件:若干进程请求的资源
死锁模型-哲学家就餐问题
死锁模型: 哲学家就餐问题:
/*
* 死锁模拟,哲学家就餐问题的简单实现
*
*
* @author 徐兵
*/
public class DeadLock {
public static void main(String arg[])
{
DeadLock dead = new DeadLock();
Thread t1=new Thread((new DeadLock()).new MyRunnable1(dead),"t1");
Thread t2=new Thread((new DeadLock()).new MyRunnable1(dead),"t2");
t1.start();
t2.start();
}
public class MyRunnable1 implements Runnable
{
DeadLock dead =new DeadLock();
public MyRunnable1(DeadLock dead) {
this.dead=dead;
}
@Override
public void run() {
dead.write(1, 2);
dead.read();
}
}
public class MyRunnable2 implements Runnable
{
DeadLock dead =new DeadLock();
public MyRunnable2(DeadLock dead){
this.dead=dead;
}
@Override
public void run() {
dead.read();
dead.write(1, 2);
}
}
private String resourceA =new String();//资源,注意:要为同一对象
private String resourceB =new String();
public void read() {
synchronized (resourceA) {
System.out.println("getA");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resourceB) {
System.out.println("getB");
}
}
}
public void write(int a,int b) {
synchronized (resourceB) {
System.out.println("getB ");
try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
synchronized (resourceA) {
System.out.println("getA");
}
}
}
}
https://github.com/xnzaa/xnzaa.github.io/raw/master/_images/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/RegexCpp.png
针对哲学家就餐问题,也存在很多解决方法,目前被大家所认可的是下面3种方法:
服务生法、资源分级法、Chandy/Misra法(哲学家沟通法)
死锁避免的3种方法
- 死锁的预防
- 死锁的避免
- 死锁的检测与忽略
银行家算法
银行家算法源代码及其解释
银行家算法是一种预分配算法,银行家类似操作系统,线程所需的资源就好比银行里的钱,线程申请资源好比是从银行里贷款。
当进程首次申请资源时,操作系统会测试进程所需的最大资源,若超过系统现有资源的总和,则推迟分配,否者正常分配。
当进程运行中再次申请资源时,操作系统会判断 (这次资源的申请量)+(进程已占有的资源量)>(进程的最大需求量)? 如超过则拒绝分配。
若没超过,再比较本次申请的资源与系统目前的资源总量,若超过,则推迟分配,否者正常分配。
银行家算法有一个很大的问题就是,需要预先知道程序所需的所有资源!
/*
* 银行家算法模拟
*
* @author 徐兵
*/
public class ProblemBanker {
//这个算法没写!
//主要原因:算法思想本身的编程实现,使用模拟资源,线程调度内容无关,所以没写!
//次要原因:我懒~
//哈哈,你来完成,怎么样?
}
当然,还存在很多其他解决死锁的方法:比如一次性分配等,但是都会不同程度地降低程序的并发性,降低效率!
上一章: 同步互斥及模型实现
下一章: 其他