龙空技术网

Java面试必考问题:如何处理死锁问题

微说互联网 1095

前言:

目前大家对“java死锁例子”大约比较珍视,姐妹们都想要分析一些“java死锁例子”的相关文章。那么小编同时在网上搜集了一些对于“java死锁例子””的相关文章,希望咱们能喜欢,咱们一起来学习一下吧!

死锁好像交通堵塞

死锁的产生有四个必要条件:互斥条件、占有并等待条件、不可剥夺条件、循环等待条件。处理死锁问题的方法主要是预防死锁、避免死锁、检测死锁和解除死锁。

处理死锁的方法

处理死锁的方法

预防死锁:通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或几个条件,来防止死锁的发生。

避免死锁:在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免死锁的发生。

检测死锁:允许系统在运行过程中发生死锁,但可设置检测机构及时检测死锁的发生,并采取适当措施加以清除。

解除死锁:当检测出死锁后,便采取适当措施将进程从死锁状态中解脱出来。

本质上来说,处理死锁的方法基本上都是破坏或避免死锁产生的必要条件。

如何破坏死锁条件(以下线程也可以换成进程)

破坏“互斥”条件:在系统里取消互斥。若资源不被一个线程独占使用,那么死锁是肯定不会发生的。比如不使用互斥锁,而使用乐观锁。但一般来说“互斥”条件往往无法避免,在死锁预防里主要是破坏其他几个必要条件。

破坏“占有并等待”条件:就是在系统中不允许线程在已获得某种资源的情况下,申请其他资源。一种方法是线程创建时,就要求所需的全部资源,要么全部占有,要么什么都不占有;另一种方法是要求每个线程提出新的资源申请前,先释放它所占有的资源。

破坏“不可抢占”条件:就是允许对资源实行抢夺。比如当线程优先级不同时,系统可以抢占低优先级线程所占据的资源,给高优先级线程。

破坏“循环等待”条件:一种方法是将系统中的资源编号,对资源的申请必须按照顺序进行,这样做就能保证系统不出现死锁。

限定加锁顺序

如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。限定加锁顺序是一种常用的避免死锁的方法。

Thread 1:  lock A   lock BThread 2:   wait for A   lock C (when A locked)Thread 3:   wait for A   wait for B   wait for C

如上例中,线程2和线程3只有在获取了锁A之后才能尝试获取锁C。线程1先占有了锁A,所以线程2和3需要一直等到锁A被释放。等线程1释放锁A后,线程2和线程3竞争锁A,只有成功获得锁A后,才能继续尝试对B或C加锁。这样就打破了循环等待条件,避免了死锁。

不过这种方法需要事先知道所有可能用到的锁,并设置好加锁顺序。很多时候我们可能是无法预知的。

控制加锁时间

看下面这个例子,线程1占有锁A并尝试获取锁B,线程2占有锁B并尝试获取锁A,这是一个典型的死锁。

Thread 1 locks AThread 2 locks BThread 1 attempts to lock B but is blockedThread 2 attempts to lock A but is blockedThread 1's lock attempt on B times outThread 1 backs up and releases A as wellThread 1 waits randomly (e.g. 257 millis) before retrying.Thread 2's lock attempt on A times outThread 2 backs up and releases B as wellThread 2 waits randomly (e.g. 43 millis) before retrying.

线程1等待锁B的时间超时后,线程1就会回退(back up),并释放(release)锁A,等过一段随机的时间间隔后再进行重试。

线程2等待锁A的时间超时后,线程2也会回退,并释放锁B。线程2也会等一段时间重试,线程2的时间比线程1短,因此线程2会先拿到锁A和锁B。

通过限制占用锁时间,主动回退打破了占有并等待条件。我们可以使用Lock类中的tryLock方法去尝试获取锁,这个方法可以指定一个超时时限,如果超时,我们可以触发回退动作。

我会持续更新关于物联网、云原生以及数字科技方面的文章,用简单的语言描述复杂的技术,也会偶尔发表一下对IT产业的看法,欢迎大家关注,谢谢。

标签: #java死锁例子