您当前的位置:首页 > 精选问答 > 内容

JAVA多线程死锁问题?C# 线程死锁的原因

产生死锁可能性的最根本原因是:线程在获得一个锁L1的情况下再去申请另外一个锁L2,产生进程死锁的原因是什么如何接触死锁原因:1、竞争不可抢占性资源p1已经打开F1,死锁的另一个原因是默认的锁申请操作是阻塞的,很多情况的场景都不会很明显的让我们察觉到会存在发生死锁的可能性,而另一个线程T2持有锁L2并且申请获得锁L1,另外一个线程都不会去获得id=1上的锁(因为他没有获得id=100上的锁),一个线程T1持有锁L1并且申请获得锁L2,而线程T3持有锁L3并且申请获得锁L1  此时如果T3申请锁L1失败。

JAVA多线程死锁问题

  1. Java中导致死锁的原因  Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2永远被阻塞了。导致了死锁。这是最容易理解也是最简单的死锁的形式。但是实际环境中的死锁往往比这个复杂的多。可能会有多个线程形成了一个死锁的环路,比如:线程T1持有锁L1并且申请获得锁L2,而线程T2持有锁L2并且申请获得锁L3,而线程T3持有锁L3并且申请获得锁L1,这样导致了一个锁依赖的环路:T1依赖T2的锁L2,T2依赖T3的锁L3,而T3依赖T1的锁L1。从而导致了死锁。  从这两个例子,我们可以得出结论,产生死锁可能性的最根本原因是:线程在获得一个锁L1的情况下再去申请另外一个锁L2,也就是锁L1想要包含了锁L2,也就是说在获得了锁L1,并且没有释放锁L1的情况下,又去申请获得锁L2,这个是产生死锁的最根本原因。另一个原因是默认的锁申请操作是阻塞的。  2. Java中如何避免死锁  既然我们知道了产生死锁可能性的原因,那么就可以在编码时进行规避。Java是面向对象的编程语言,程序的最小单元是对象,对象封装了数据和操作,所以Java中的锁一般也是以对象为单位的,对象的内置锁保护对象中的数据的并发访问。所以如果我们能够避免在对象的同步方法中调用其它对象的同步方法,那么就可以避免死锁产生的可能性。如下所示的代码,就存在死锁的可能性:  public class ClassB { private String address; // ... public synchronized void method1(){ // do something } // ... ...}  public class ClassA { private int id; private String name; private ClassB b; // ... public synchronized void m1(){ // do something b.method1(); } // ... ...}  上面的ClassA.m1()方法,在对象的同步方法中又调用了ClassB的同步方法method1(),所以存在死锁发生的可能性。我们可以修改如下,避免死锁:  public class ClassA { private int id; private String name; private ClassB b; // ... public void m2(){ synchronized(this){ // do something } b.method1(); } // ... ...}  这样的话减小了锁定的范围,两个锁的申请就没有发生交叉,避免了死锁的可能性,这是最理性的情况,因为锁没有发生交叉。但是有时是不允许我们这样做的。此时,如果只有ClassA中只有一个m1这样的方法,需要同时获得两个对象上的锁,并且不会将实例属性 b 溢出(return b;),而是将实例属性 b 封闭在对象中,那么也不会发生死锁。因为无法形成死锁的闭环。但是如果ClassA中有多个方法需要同时获得两个对象上的锁,那么这些方法就必须以相同的顺序获得锁。  比如银行转账的场景下,我们必须同时获得两个账户上的锁,才能进行操作,两个锁的申请必须发生交叉。这时我们也可以打破死锁的那个闭环,在涉及到要同时申请两个锁的方法中,总是以相同的顺序来申请锁,比如总是先申请 id 大的账户上的锁 ,然后再申请 id 小的账户上的锁,这样就无法形成导致死锁的那个闭环。  public class Account { private int id; // 主键 private String name; private double balance; public void transfer(Account from, Account to, double money){ if(from.getId() 》 to.getId()){ synchronized(from){ synchronized(to){ // transfer } } }else{ synchronized(to){ synchronized(from){ // transfer } } } } public int getId() { return id; }}  这样的话,即使发生了两个账户比如 id=1的和id=100的两个账户相互转账,因为不管是哪个线程先获得了id=100上的锁,另外一个线程都不会去获得id=1上的锁(因为他没有获得id=100上的锁),只能是哪个线程先获得id=100上的锁,哪个线程就先进行转账。这里除了使用id之外,如果没有类似id这样的属性可以比较,那么也可以使用对象的hashCode()的值来进行比较。  上面我们说到,死锁的另一个原因是默认的锁申请操作是阻塞的,所以如果我们不使用默认阻塞的锁,也是可以避免死锁的。我们可以使用ReentrantLock.tryLock()方法,在一个循环中,如果tryLock()返回失败,那么就释放以及获得的锁,并睡眠一小段时间。这样就打破了死锁的闭环。  比如:线程T1持有锁L1并且申请获得锁L2,而线程T2持有锁L2并且申请获得锁L3,而线程T3持有锁L3并且申请获得锁L1  此时如果T3申请锁L1失败,那么T3释放锁L3,并进行睡眠,那么T2就可以获得L3了,然后T2执行完之后释放L2, L3,所以T1也可以获得L2了执行完然后释放锁L1, L2,然后T3睡眠醒来,也可以获得L1, L3了。打破了死锁的闭环。  这些情况,都还是比较好处理的,因为它们都是相关的,我们很容易意识到这里有发生死锁的可能性,从而可以加以防备。很多情况的场景都不会很明显的让我们察觉到会存在发生死锁的可能性。所以我们还是要注意:  一旦我们在一个同步方法中,或者说在一个锁的保护的范围中,调用了其它对象的方法时,就要十而分的小心:  1)如果其它对象的这个方法会消耗比较长的时间,那么就会导致锁被我们持有了很长的时间;  2)如果其它对象的这个方法是一个同步方法,那么就要注意避免发生死锁的可能性了;  最好是能够避免在一个同步方法中调用其它对象的延时方法和同步方法。如果不能避免,就要采取上面说到的编码技巧,打破死锁的闭环,防止死锁的发生。同时我们还可以尽量使用“不可变对象”来避免锁的使用,在某些情况下还可以避免对象的共享,比如 new 一个新的对象代替共享的对象,因为锁一般是对象上的,对象不相同了,也就可以避免死锁,另外尽量避免使用静态同步方法,因为静态同步相当于全局锁。还有一些封闭技术可以使用:比如堆栈封闭,线程封闭,ThreadLocal,这些技术可以减少对象的共享,也就减少了死锁的可能性。

C# 线程死锁的原因

这是经典的死锁模型呀。对于A、B两个资源,有a、b两个进程都想占有。a加锁的顺序是A、B,b加锁的顺序是B、A。那么试想,a成功取得A之后,系统进程切换了,b开始运行,并成功取得B,它想接着取得A,就无法成功,因为A已被a取得,所以一般来讲,b进入睡眠等待A被释放。而A会被释放吗?不可能了。因为a无论何时再次唤醒,它都要坚定地去获得B,而不会去释放A,同理,B在b手中,也不会得到释放,于是a、b两个进程就相互等待,进入死锁状态。

产生进程死锁的原因是什么如何接触死锁

原因:

1、竞争不可抢占性资源

p1已经打开F1,想去打开F2,p2已经打开F2,想去打开F1,但是F1和F2都是不可抢占的,这是发生死锁。

2、竞争可消耗资源引起死锁

进程间通信,如果顺序不当,会产生死锁,比如p1发消息m1给p2,p1接收p3的消息m3,p2接收p1的m1,发m2给p3,p3,以此类推,如果进程之间是先发信息的那么可以完成通信,但是如果是先接收信息就会产生死锁。

3、进程推进顺序不当

进程在运行过程中,请求和释放资源的顺序不当,也同样会导致产生进程死锁。

接触:必备条件

1、互斥性:线程对资源的占有是排他性的,一个资源只能被一个线程占有,直到释放。

2、请求和保持条件:一个线程对请求被占有资源发生阻塞时,对已经获得的资源不释放。

3、不剥夺:一个线程在释放资源之前,其他的线程无法剥夺占用。

4、循环等待:发生死锁时,线程进入死循环,永久阻塞。

扩展资料

死锁的检测

1、每个进程、每个资源制定唯一编号。

2、设定一张资源分配表,记录各进程与占用资源之间的关系。

3、设置一张进程等待表,记录各进程与要申请资源之间的关系。

死锁的解除

1、抢占资源,从一个或多个进程中抢占足够数量的资源,分配给死锁进程,以解除死锁状态。

2、终止(或撤销)进程,终止(或撤销)系统中的一个或多个死锁进程,直至打破循环环路,使系统从死锁状态解脱出来。

线程死锁的概念 为什么产生死锁的原因及产生死锁的四个必要条件

产生死锁的原因主要是因为系统资源不足。进程运行推进的顺序不合适。资源分配不当等。如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。产生死锁的四个必要条件互斥条件:一个资源每次只能被一个进程使用。请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。


声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,谢谢。

上一篇: oppo 新机reno(OPPOReno78+256和12+256哪个好)

下一篇: wsastartup(为什么无法在线程中创建socketWSAstartup函数总是无法初始化)



推荐阅读

网站内容来自网络,如有侵权请联系我们,立即删除! | 软文发布 | 粤ICP备2021106084号