前言:
目前大家对“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死锁例子