线程死锁问题

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、附:

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

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

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

时间: 2024-09-27 17:03:18

线程死锁问题的相关文章

java的线程死锁

由于线程可能进入堵塞状态,而且由于对象可能拥有"同步"方法--除非同步锁定被解除,否则线程不能访问那个对象--所以一个线程完全可能等候另一个对象,而另一个对象又在等候下一个对象,以此类推.这个"等候"链最可怕的情形就是进入封闭状态--最后那个对象等候的是第一个对象!此时,所有线程都会陷入无休止的相互等待状态,大家都动弹不得.我们将这种情况称为"死锁".尽管这种情况并非经常出现,但一旦碰到,程序的调试将变得异常艰难. 就语言本身来说,尚未直接提供防

多线程专题之线程死锁原因之谜

引子:线程死锁曾是多少程序员的噩梦,每每为此食不甘味,夜不成寐,一句话:苦不堪言.本文从几个场景入手,试图解开产生死锁的原因之谜. 教科书:说的很具体,理解很抽象 关于死锁产生的原因<操作系统>中有比较好的说明: (1)因为系统资源不足. (2)进程运行推进的顺序不合适. (3)资源分配不当等. 关于死锁出现的必要条件也有比较具体的说明: (1)互斥条件:一个资源每次只能被一个进程使用. (2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放. (3)不剥夺条件:进程已获得的

java 进程死锁-java 模拟线程死锁实验

问题描述 java 模拟线程死锁实验 我想两个线程一直在售票知道票卖光,但是用while()控制循环后出现 售票2进入共享区 当前没有车票可以出售! 售票2进入共享区 当前没有车票可以出售! 售票2进入共享区 ..... 具体代码如下 package com; public class SaleThread extends Thread{ private String name; ShareResource shareresource; public SaleThread(){} public

线程死锁的质疑

问题描述 在网上搜到一篇线程死锁的博客,后来发现,两个线程并不会发生死锁.我的理由是Threadthread01=newThread(run01);Threadthread02=newThread(run02);这两个线程构造函数传进来的参数Runnable对象run01,run02,会分别创建obj1.obj2,也就说两个线程通过synchronized(obj1)和synchronized(obj2)并不能实现互斥.[/color]packagecn.thread;publicclassTh

线程 死锁-一个关于线程挂起、唤醒的问题

问题描述 一个关于线程挂起.唤醒的问题 在windows平台上, 线程的挂起可以使用API SuspendThread()函数,而减少线程的挂起计数则可以使用ResumeThread()函数,但是今天却发现了其中隐藏的问题:1. 现有一个主线程和子线程A,在主线程中调用SuspengThread()使得A被挂起:2. 主线程中调用ResumeThread()使得A恢复运行:3. 主线程立刻调用SuspendThread()意图挂起A时,主线程竟然被阻塞在了调用处!也就是说产生了deadlock!

synchronized-Java的一个简单线程死锁问题

问题描述 Java的一个简单线程死锁问题 class BadPerson { public synchronized void say(GoodPerson good) { System.out.println(""把钱给我,放了你的人.""); good.give(); } public synchronized void give() { System.out.println(""得到了钱,同时被警察抓了.""); }}c

java模拟线程死锁

  /* java线程死锁的模拟 两个线程同时请求对方的对方释放监视对象 造成死锁 请在 虚拟机 单核下运行 */ class  Test  {  public static void main(String []args)  throws Exception    {      ThreadOne td=new ThreadOne();      new Thread(td).start();      Thread.sleep(3);      new Thread(td).start();

c#如何避免线程死锁

大多数的死锁都是因为代码允许以不一致的方式锁定资源.例如我们要将一个账户的资金转到另一个账户中.Monitor类提供同步对对象的访问的机制.类通过向单个线程授予对象锁来控制对对象的访问.对象锁提供限制访问代码块(通常称为临界区)的能力.当一个线程拥有对象的锁时,其他任何线程都不能获取该锁.还可以使用Monitor来确保不会允许其他任何线程访问正在由锁的所有者执行的应用程序代码节,除非另一个线程正在使用其他的锁定对象执行该代码.示例代码如下: public void Transer(Account

java 线程死锁

问题描述 publicvoidrun(){for(TlcLogStddelivertable:listTable){System.out.println("正在传送当前第"+DatatransfersBo.n+"条数据");System.out.println("手机号---------"+table.getVc2srcmobile());System.out.println("手机内容"+table.getVc2messag