死锁与活锁
1、什么是死锁?死锁产生的条件?
什么是死锁:
- 在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。
- 通俗的讲就是两个或多个进程无限期的阻塞、相互等待的一种状态。
死锁产生的四个必要条件:(有一个条件不成立,则不会产生死锁)
- 互斥条件:一个资源一次只能被一个进程使用;
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源保持不放;
- 不剥夺条件:进程获得的资源,在未完全使用完之前,不能强行剥夺;
- 循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系。
2、如何解决线程死锁?
只要破坏产生死锁的四个条件中的其中一个就可以了
- 破坏互斥条件 :这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)
- 破坏请求与保持条件: 一次性申请所有的资源。
- 破坏不剥夺条件: 占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
- 破坏循环等待条件: 靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。
3、如何检测死锁?
可以通过检测有向图中是否存在环来检测,从一个节点出发进行 dfs,对访问过的节点进行标记,如果访问到了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。
4、如何解除死锁?
解除死锁的主要方法有:
1. 资源剥夺法:
- 挂起(暂时放到外存上)某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。
- 但是应防止被挂起的进程长时间得不到资源而饥饿。
2. 撤销进程法(或称终止进程法):
- 强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资源。
- 这种方式的优点是实现简单,但所付出的代价可能会很大。因为有些进程可能已经运行了很长时间,已经接近结束了,一旦被终止可谓功亏一篑,以后还得从头再来。
3. 进程回退法:
- 让一个或多个死锁进程回退到足以避免死锁的地步。
- 要求系统要记录进程的历史信息,设置还原点。
5、举一个死锁的例子?
假设我们有两个线程,分别是线程A和线程B,假设线程A现在持有了锁A,线程B持有了锁B,然后线程A尝试去获取锁B,当然它获取不到,因为线程B还没有释放锁B。然后线程B又来尝试获取锁A,同样线程B也获取不到锁A,因为锁A已经被线程A持有了。这样一来,线程A和线程B就发生了死锁,因为它们都相互持有对方想要的资源,却又不释放自己手中的资源,形成相互等待,而且会一直等待下去。
6、什么是活锁?活锁和死锁的区别?
概念:
- 活锁指的是,两个线程都是处于活跃状态(Runnable),但是呢两个线程分别相互推脱任务,导致线程繁忙,最终程序无法继续向前运行。
活锁和死锁的区别
- 处于活锁的实体是在不断的改变状态,这就是所谓的“ 活” , 而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
解决活锁
- 为解决活锁可以引入一些随机性,例如如果检测到冲突,那么就暂停随机的一定时间进行重试,这会大大减少碰撞的可能性。
7、什么是饥饿?死锁和饥饿的共同点与区别?
饥饿:
- 由于长期得不到想要的资源,某进程无法向前推进的现象。比如:在短进程优先(SPF)算法中,若有源源不断的短进程到来,则长进程将一直得不到处理机,从而发生长进程“饥饿。
共同点:
- 都是进程无法顺利向前推进的现象 (故意设计的死循环除外)
区别:
- 死锁一定是“循环等待对方手里的资源”导致的,因此如果有死锁现象,那至少有两个或两个以上的进程同时发生死锁。另外,发生死锁的进程一定处于阻塞态。
- 可能只有一个进程发生饥饿。发生饥饿的进程既可能是阻塞态(如长期得不到需要的I/o设备),也可能是就绪态(长期得不到处理机)