1、引言
5个哲学家的故事:
5个哲学家去吃饭,菜饭都上齐了,筷子也上了,但是一人只有一只筷子,每个人,先思考一会,把筷子借给别人,然后,别人吃完了,自己再吃。但是假如这5个人都饿了,他们就会拿起自己的筷子,而筷子只有一只,大家都在等待这个别人放下那一只筷子,然后好拿过来吃饭,而没有任何一个人愿意先放下筷子,所以,就出现了死锁。
所以,死锁就是两个线程都掌握着另一个线程下一步需要访问的资源,而两个线程却都不愿意放弃自己手中的资源而导致的线程阻塞。
2、死锁示例
代码详解放注释中:
(1)创建两个锁对象
<span style="font-size:18px;">public class MyLock { //首先,创造出我的两个锁,就是创造两个任意对象,是我的a锁和b锁 public static final Object objA = new Object(); public static final Object objB = new Object(); }</span>
(2)死锁类内部示范
<span style="font-size:18px;">public class DieLock extends Thread { //定义标记 private boolean flag; //构造方法传入标记值 public DieLock(boolean flag) { this.flag = flag; } //重写run方法 @Override public void run() { if (flag) { synchronized (MyLock.objA) { System.out.println("true -- objA");//d1执行到这里,接下来该访问b锁,但是b锁在d2的手里,d2接下来该访问 //a锁,但是a锁在d1的手里,然后两个都不想放锁,谁也执行不了,就形成了死锁。--stop synchronized (MyLock.objB) { //d1 System.out.println("true -- objB"); } } } else { synchronized (MyLock.objB) { System.out.println("false -- objB");//d2 synchronized (MyLock.objA) { //d2 System.out.println("false -- objA"); } } } } }</span>
(3)测试类
<span style="font-size:18px;">public class DieLockDemo { //测试类 public static void main(String[] args) { //创建对象 DieLock d1 = new DieLock(true); DieLock d2 = new DieLock(false); //start()方法内部会自动调用run方法 d1.start(); d2.start(); } }</span>
由于d1和d2都在等待对方所握有的资源,而双方都不选择主动放弃,所以就形成了死锁。
3、死锁的发生条件
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
4、死锁的解决办法
首先看一个进程的五态图:
等待资源的状态就是线程的阻塞状态,操作系统中解决死锁的办法有很多:
死锁预防
破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。
死锁避免
避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。
预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得 较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则,进程等待。最具有代表性的避免死锁算法是银行家算法。
死锁检测
死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。这需要首先为每个进程和每个资源指定一个唯一的号码;然后建立资源分配表和进程等待表。
死锁解除
这是与死锁检测结合使用的,它使用的方式就是剥夺。即将某进程所拥有的资源强行收回,分配给其他的进程。当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常采用的方法有:
剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。
5、附:
死锁预防的缺点:如果都要等到资源齐全时候才能开始运行,就会大大降低系统运行的效率,如果有几个资源就运行几个资源,效率就会提升。
系统效率:指系统的资源是否全部利用上,利用率高则效率高;利用率低则效率低。
系统开销:需要额外做一些判断或处理操作,从而增加了系统的工作量,这样叫做增加了系统开销。