1、引言

  5个哲学家的故事:

  5个哲学家去吃饭,菜饭都上齐了,筷子也上了,但是一人只有一只筷子,每个人,先思考一会,把筷子借给别人,然后,别人吃完了,自己再吃。但是假如这5个人都饿了,他们就会拿起自己的筷子,而筷子只有一只,大家都在等待这个别人放下那一只筷子,然后好拿过来吃饭,而没有任何一个人愿意先放下筷子,所以,就出现了死锁。

  所以,死锁就是两个线程都掌握着另一个线程下一步需要访问的资源,而两个线程却都不愿意放弃自己手中的资源而导致的线程阻塞。

2、死锁示例

  代码详解放注释中:

  (1)创建两个锁对象

<span >public class MyLock {

	//首先,创造出我的两个锁,就是创造两个任意对象,是我的a锁和b锁
	public static final Object objA = new Object();
	public static final Object objB = new Object();
}</span>

  (2)死锁类内部示范

<span >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 >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、死锁的发生条件

            

iOS 线程主动阻塞_iOS 线程主动阻塞

  互斥条件:一个资源每次只能被一个进程使用。

  请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

  不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

  循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

4、死锁的解决办法

  首先看一个进程的五态图:

iOS 线程主动阻塞_死锁_02


  等待资源的状态就是线程的阻塞状态,操作系统中解决死锁的办法有很多:

  死锁预防

    破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。

   死锁避免

    避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。

    预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得 较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则,进程等待。最具有代表性的避免死锁算法是银行家算法。

  死锁检测

    死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。这需要首先为每个进程和每个资源指定一个唯一的号码;然后建立资源分配表和进程等待表。

  死锁解除

    这是与死锁检测结合使用的,它使用的方式就是剥夺。即将某进程所拥有的资源强行收回,分配给其他的进程。当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常采用的方法有:

    剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;

    撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。

5、附:

  死锁预防的缺点:如果都要等到资源齐全时候才能开始运行,就会大大降低系统运行的效率,如果有几个资源就运行几个资源,效率就会提升。

  系统效率:指系统的资源是否全部利用上,利用率高则效率高;利用率低则效率低。

  系统开销:需要额外做一些判断或处理操作,从而增加了系统的工作量,这样叫做增加了系统开销。