线程活跃性问题(死锁、活锁、饥饿)

1.什么是“死锁”?

        在多线程并发中,两个及以上线程互相持有对方所需要的资源又不主动释放,导致程序进入无尽的阻塞这就是“死锁”;

2.写一段“死锁”代码

import java.util.concurrent.TimeUnit;
/*** 写一段必然发生死锁代码*/
public class MustDeadLock implements Runnable{// 定义标记位,用于指示不同的线程获取不同的锁int flag = 1;// 定义两把锁static Object lockOne = new Object();static Object lockTwo = new Object();@Overridepublic void run() {if(flag == 1){synchronized (lockOne){ // 获取锁“lockOne”System.out.println("线程一获取到了锁lockOne");try {TimeUnit.MILLISECONDS.sleep(500); // 休眠的意义在于等待两个线程都已成功获取了“第一把锁”} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockTwo){ // 获取锁“lockOne”成功后,尝试获取锁“lockTwo”System.out.println("线程一获取到了两把锁...");}}}if(flag == 2){synchronized (lockTwo){ // 获锁“lockTwo”System.out.println("线程二获取到了锁lockTwo");try {TimeUnit.MILLISECONDS.sleep(500); // 休眠的意义在于等待两个线程都已成功获取了“第一把锁”} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockOne){ // 获取锁“lockTwo”成功后,尝试获取锁“lockOne”System.out.println("线程二获取到了两把锁...");}}}}// 测试public static void main(String[] args) {MustDeadLock r1 = new MustDeadLock();MustDeadLock r2 = new MustDeadLock();r1.flag = 1; // 设置标记位r2.flag = 2; // // 设置标记位Thread t1 = new Thread(r1);Thread t2 = new Thread(r2);t1.start();t2.start();}
}

3.如何定位“死锁”

方式一:使用Java自带的jstack命令

使用前提:已知死锁可能发生在哪个类中;    

流程:

        1.运行上面的死锁代码,让程序先出现死锁问题;

        2.进入程序所使用JDK的bin目录,执行jps获取到其进程号;

        3.再执行jstack "pid" 就可以查找到

方式二:使用ThreadMXBean定位死锁

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.TimeUnit;
/*** 使用ThreadMXBean检测死锁*/
public class ThreadMXBeanDetection implements Runnable{// 定义标记位,用于指示不同的线程获取不同的锁int flag = 1;// 定义两把锁static Object lockOne = new Object();static Object lockTwo = new Object();@Overridepublic void run() {if(flag == 1){synchronized (lockOne){ // 获取锁“lockOne”System.out.println("线程一获取到了锁lockOne");try {TimeUnit.MILLISECONDS.sleep(500); // 休眠的意义在于等待两个线程都已成功获取了“第一把锁”} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockTwo){ // 获取锁“lockOne”成功后,尝试获取锁“lockTwo”System.out.println("线程一获取到了两把锁...");}}}if(flag == 2){synchronized (lockTwo){ // 获锁“lockTwo”System.out.println("线程二获取到了锁lockTwo");try {TimeUnit.MILLISECONDS.sleep(500); // 休眠的意义在于等待两个线程都已成功获取了“第一把锁”} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockOne){ // 获取锁“lockTwo”成功后,尝试获取锁“lockOne”System.out.println("线程二获取到了两把锁...");}}}}// 测试public static void main(String[] args) throws InterruptedException {ThreadMXBeanDetection r1 = new ThreadMXBeanDetection();ThreadMXBeanDetection r2 = new ThreadMXBeanDetection();r1.flag = 1; // 设置标记位r2.flag = 2; // // 设置标记位Thread t1 = new Thread(r1,"线程一");Thread t2 = new Thread(r2,"线程二");t1.start();t2.start();TimeUnit.MILLISECONDS.sleep(500);// 此处开始使用ThreadMXBean对象检测“死锁”ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();if(deadlockedThreads != null && deadlockedThreads.length > 0){for (int i = 0; i < deadlockedThreads.length; i++) {ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]); // 线程信息String threadName = threadInfo.getThreadName();System.out.println("发生“死锁”的线程名字为:" + threadName);// TODO 当然已经检测到发生“死锁”问题,那你可以发送邮件给开发者让其处理,也可以让其自动重启;}}}
}

4.修复“死锁”的策略

1.鸵鸟策略

        说明:如果发生“死锁”的概率极低,那我们就直接忽略它,直到“死锁”发生时再去处理它;

2.避免策略

        说明:“哲学家就餐例子”换手方案;

3.检测与恢复策略

        说明:允许发生“死锁”,发生死锁时“我”帮你检测出来并恢复;

        检测算法:锁的调用链路图;
        恢复方法一:进程终止(逐个终止线程直到死锁消除);

                                根据我们自己判断的优先级去终止一些对业务影响较小且造成死锁的线程;

        恢复方法二:资源抢占(把发出去的锁收回来,让线程回退几步);

                                缺点:可能同一个线程一直被抢占造成该线程一直无法获取到锁“饥饿”问题;

5.实际开发中如何避免“死锁”

        1.设置超时时间

        2.多使用并发类,而不是自己去设计锁

                ConcurrentHashMap、ConcurrentLinkedQueue、AtomicBoolean等...

        3.降低锁的粒度尽量不要几个功能用同一把锁(专锁专用)   

        4.能使用同步代码块就不使用同步方法

                使用同步代码块可以自己指定锁对象,锁的范围小于等于同步方法;

        5.给线程起一个有意义的名字

                如果发现“死锁”或线程安全问题时,我们根据线程名去排查问题时就可以事半功倍;

        6.避免锁的嵌套

        7.分配“锁”资源前看能不能收回

                “银行家算法”,分配锁之前看能不能收回锁假如分配锁后能收回锁,就算发生死锁我们

                也可以让线程执行回退两步回收锁,避免死锁;

6.什么是“活锁”?

        活锁表示执行任务的线程没有被阻塞,但由于某些条件没有被满足导致重复的去尝试执行任务(失败->尝试->失败),“活锁”与“死锁”的区别在于“死锁”线程一直处于等待状态而“活锁”执行线程在不断的执行;

7.如何解决“活锁”

        加入随机因素;加入随机数让线程双方不再让渡资源优先随机使其一方先执行后再释放锁;

        活锁案例:活锁——牛郎织女的幸福生活_牛郎织女幸福生活-CSDN博客文章浏览阅读692次,点赞4次,收藏6次。从前,有一对非常恩爱的夫妻,他们都有一颗谦让的心,但家境却不是很好,吃了上顿没下顿,于是,当他们有食物的时候,他们会优先考虑对方,如果对方饿的话,就让给对方吃,等对方吃饱了自己才吃,这种美德本身是好的,但是如果一味的谦让,也会发生一些让人啼笑皆非的事儿…_牛郎织女幸福生活https://blog.csdn.net/qq_36221788/article/details/103078399       “消息队列”就是一个活锁例子,如果消息处理失败就放在队列开头重试,由于服务出现问题导

        致该消息一直重试失败;

8.什么是“饥饿”问题?有什么影响?

        “饥饿”是指线程执行时无法获取到它所需要的资源比如(CPU),一种情况是“线程”优先级分配不合理导致部分线程一直无法被CPU调度执行;另一种情况是某线程持有某个操作的“锁”但其又处于无限循环而又不释放锁,此时其它等待获取此锁的线程获取不到就出现了饥饿问题;饥饿问题会导致服务器响应变差;

        

9.面试题:

        1.写一个必然发生“死锁”的例子

                详见本章序号2;

        2.生产中什么场景会发生“死锁”

                一个方法中获取多把锁时易发生死锁问题;或循环调用获取锁会形成一个闭合的死锁链路;

                比如库存的增减,金额的增减;

        3.死锁的四个必要条件

                1.互斥(资源只能同时被一个线程竞争获取)

                2.某线程已持有一把锁在不释放已获取到的锁,同时再去不停的获取另一把锁;

                3.其它线程不能释放这个线程已获取到的锁;

                4.循环等待(多线程中循环等待构成环路才会发生死锁)

        4.如何定位“死锁”

                详见本章序号3;

        5.有哪些解决“死锁”问题的策略

                详见本章序号4;

        6.实际工作中如何避免死锁发生

                详见本章序号5;

        7.什么是活跃性问题?活锁,死锁,饥饿有什么区别?

                 详见本章序号1、7、8;

悟空老师Java并发编程思维导图:

https://naotu.baidu.com/file/ec7748c253f4fc9d88ac1cc1e47814f3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/292667.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

01、ThreadPoolExecutor 线程池源码完整剖析 ------ 线程池工作流程、参数解析、简单创建线程池及使用演示

目录 线程池源码剖析什么是线程&#xff1f;什么是多线程&#xff1f;什么是线程池 &#xff1f;为什么需要用到线程池 &#xff1f;使用线程池有哪些优势 &#xff1f;线程的应用场景有哪些 &#xff1f; 2、线程池工作流程ThreadPoolExecutor参数详解1、核心线程数&#xff0…

【Jmeter】循环执行某个接口,接口引用的参数变量存在规律变化

变量设置成下面的值即可 ${__V(supplierId_${supplierIdNum})}

【即插即用篇】YOLOv8改进实战 | 引入 Involution(内卷),用于视觉识别的新一代神经网络!涨点神器!

YOLOv8专栏导航:点击此处跳转 前言 YOLOv8 是由 YOLOv5 的发布者 Ultralytics 发布的最新版本的 YOLO。它可用于对象检测、分割、分类任务以及大型数据集的学习,并且可以在包括 CPU 和 GPU 在内的各种硬件上执行。 YOLOv8是一种尖端的、最先进的 (SOTA) 模型,它建立在以前成…

STM32 AI 模型测试

PC仿真软件测试 我在STM32单片机上跑神经网络算法—CUBE-AI_stm32cube.ai-CSDN博客 仿真软件测试结果和真实情况差距过大 云平台测试 Home - STM32Cube.AI Developer Cloud 上传模型文件 点击Start 选择优化方式 可以跳过量化步骤&#xff0c;到Benchmark 选择合适的型号&a…

韵达快递查询入口,一键将退回件筛选出来

批量查询韵达快递单号的物流信息&#xff0c;并将退回件一键筛选出来。 所需工具&#xff1a; 一个【快递批量查询高手】软件 韵达快递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;并登录 步骤2&#xff1a;点击主界面左上角的…

AP9196 DC-DC 升压恒流电源管理芯 200W

产品说明 AP9196是一系列电路简洁的宽调光比升压调光恒流驱动器&#xff0c;适用于3-40V输入电压范围的LED照明领域。AP9196采用我司算法&#xff0c;可以实现高精度的恒流效果&#xff0c;输出电流恒流精度≤3&#xff05;&#xff0c;电压工作范围为5-40V&#xff0c;可以轻…

浅谈在线监测系统与配电能效平台在供水水厂的应用

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201800 【摘要】针对自来水厂工艺老化资金有限的问题&#xff0c;设计水厂在线监测系统&#xff0c;采用安科瑞&#xff0c;对原水滤后水、出厂水进行采样分析&#xff0c;并通过基于组态的上位机系统实现水质数据的实时监测。该系统…

数据结构和算法-红黑树(定义 性质 查找 插入 删除)

文章目录 红黑树的定义和性质为什么要发明红黑树&#xff1f;红黑树怎么考总览红黑树的定义实例&#xff1a;一颗红黑树练习&#xff1a;是否符合红黑树的要求一种可能的出题思路补充概念&#xff1a;节点黑高 红黑树的性质 红黑树的查找红黑树的插入实例小结与黑高相关的理论 …

YOLOv8涨点技巧:一种新颖的多尺度特征融合iAFF,适配小目标检测

💡💡💡本文全网独家改进:1)引入了一种新颖的多尺度特征融合iAFF;2)为了轻量级部署,和GhostConv有效结合在边缘端具有竞争力的准确性 💡💡💡在YOLOv8中如何使用 1)iAFF加入Neck替代Concat;2)Conv替换为GhostConv;3)加入C3Ghost; 💡💡💡Yolov8魔术…

Java小案例-Java实现人事管理系统

前言 《人事管理系统》该项目采用技术jsp、Struts2、Mybatis、dwr、tomcat服务器、mysql数据库 开发工具eclipse/idea。 【项目使用技术】 Struts2Mybatisdwrjqueryjscss等技术 前端使用技术&#xff1a;JSP, dwr、jquery、js、css等 后端使用技术&#xff1a;Struts2Myba…

Spring事务管理—讲解、案例、应用

简介&#xff1a;Spring事务管理和数据库的事务管理的功能作用上是一样的&#xff0c;在学习数据库时&#xff0c;为了数据完整性&#xff0c;采用了事务管理&#xff0c;即开启事务、提交事务和管理事务。在SpringBoot框架中添加一个注解 Transactional 就可以将当前方法、类和…

C语言之输入输出和字符(2)

目录 缓冲和重定向 ▇缓冲 ▇重定向 字符 转义字符 \和\"……字符和字符" 字符串字面量的写法 字符常量的写法 八进制转义字符和十六进制转义字符 字符编码 在看本节之前&#xff0c;请先看下上一章&#xff0c;做到更好地衔接。https://blog.csdn.net/W061…