大家好,又见面了,我是你们的朋友全栈君。
线程死锁
-
什么是线程死锁?死锁指的是两个或两个以上的线程在执行过程中因为争夺资源而造成的互相等待的现象。在无外力的情况下,这些线程一直会相互等待而无法继续进行工作。如图:
-
在上图中,线程A已经持有了资源2,它同时还想申请资源1,线程B已经持有了资源1,它同时还想申请资源2,所以线程A和线程B就因为相互等待对方已经持有的资源,而进入死锁状态。
-
为什么产生死锁呢?死锁产生的必备条件:
- 互斥条件:指线程对已经获得到的资源进行排它使用,如果还有其他线程想用这个资源,则必须等待。
- 请求并持有条件:指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新的资源请求已经被其他线程锁占有且为互斥。
- 不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其他线程所抢占,只有在自己使用完后才能由自己释放。
- 环路等待条件:指发生死锁的时候必然存在一个线程-资源的环形链,即线程集合{T0,T1,T2,…,Tn}中的T0等待T1占用的资源,T1等待T2占用的一个资源,以此类推,Tn等待T0占用的一个资源。
package com.heiye.learn1; import java.util.logging.Logger; public class DeadLockTest2 { //创建资源 private static Object resourceA = new Object(); private static Object resourceB = new Object(); public static void main(String[] args) { Logger logger = Logger.getLogger(DeadLockTest2.class.toString()); //创建线程A Thread threadA = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { logger.info(Thread.currentThread() + " get ResourceA"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } logger.info(Thread.currentThread() + " waiting get ResourceB"); synchronized (resourceB) { logger.info(Thread.currentThread() + "get ResourceB"); } } } }); //创建线程B Thread threadB = new Thread(new Runnable() { @Override public void run() { synchronized (resourceB) { logger.info(Thread.currentThread() + " get ResourceB"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } logger.info(Thread.currentThread() + " waiting get ResourceA"); synchronized (resourceA) { logger.info(Thread.currentThread() + "get ResourceA"); } } } }); //启动线程 threadA.start(); threadB.start(); } }
-
Thread-2是线程B,Thread-1是线程A,从输出结果可知,线程调度器首先调度了线程B,也就是吧CPU时间片资源分配给了B,线程B通过synchronized(ResourceB)获取到了B资源锁,然后休眠1s是为了保证线程B为了获取资源A之前先让线程A获取资源A,这样线程A获取到了资源A,线程B获取到了资源B,但是线程暗想获取资源B的资源,线B又想获取资源A的资源,这样就互相等待想入了死锁。
-
那么如何避免线程死锁呢?只需要破坏掉至少一个构造死锁的条件就可以了。造成死锁的原因其实和申请自愿的顺序有着很大的关系,使用资源申请的有序性原则就可以避免死锁,那么什么是资源申请的有序性?我们对线程B的代码进行修改
//创建线程B Thread threadB = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { logger.info(Thread.currentThread() + " get ResourceB"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } logger.info(Thread.currentThread() + " waiting get ResourceA"); synchronized (resourceB) { logger.info(Thread.currentThread() + "get ResourceA"); } } } });
-
在上面的代码中,假如线程A和线程B同时执行了synchronized(resourceA),只有一个线程可以获取到resourceA上的监视器,假如线程A获取到了,那么线程B就会阻塞而不会再去获取资源B,线程A获取到resourceA的监视器后回去申请resourceB资源,这时候线程A是获取到的,线程A获取到resourceB并使用完之后会放弃对资源resourceB的持有,然后再释放resourceA的持有,释放resourceA后线程B才会被从阻塞阶段变为激活状态。所以资源的有序性破坏了资源的请求并持有条件和环路等待条件,因此避免了死锁。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/154418.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...