死锁是什么?死锁的字节码指令了解?

news2024/11/27 13:31:13

用幽默浅显的言语来说死锁

半生:我已经拿到了机考的第一名,就差笔试第一名了

小一:我已经拿到了笔试的第一名,就差机考第一名了

面试官:我很看好你俩,继续"干", 同时拿到2个的第一名才能拿到offer,进入我XX大厂

半生:小一,你的笔试第一名,让我可好?

小一:做梦,我还要你的机考第一名呢!

半生:就你那水平,zz

小一:WTF,来干一架

半生:来呀,who怕who

于是:死锁产生了,狭路相逢勇者胜,电脑死机了。

什么是死锁?死锁的产生条件是什么?

1.死锁是指两个或多个线程互相等待对方释放所持有的资源,从而导致进程无法继续执行的一种情况。具体来说,死锁发生时线程会进入一个永久等待的状态,无法继续执行并最终导致程序无响应或崩溃。

2.产生死锁的条件,通常被称为死锁的四个必要条件,包括:

  1. 互斥条件(Mutual Exclusion):至少有一个资源被多个线程独占,也就是说一个资源同时只能被一个线程占用。
  2. 请求与保持条件(Hold and Wait):线程已经持有了至少一个资源,并且在等待获取其他线程占有的资源。
  3. 不可剥夺条件(No Preemption):已经获得的资源不能被其他线程抢占,只能由占有它的线程显示地释放。
  4. 循环等待条件(Circular Wait):多个线程形成一个循环等待的等待链,即每个线程都在等待下一个线程所持有的资源。

只有当上述四个条件同时满足时,死锁才可能发生。

下面上一段代码(有问题的代码,考考你们的眼力跟基本功)

 public static void main(String[] args) {
        final Object resource1 = new Object();
        final Object resource2 = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1:锁住offer1");
            }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("Thread 1:锁住offer2");
                }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2:锁住offer2");
            }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource1) {
                    System.out.println("Thread 2:锁住offer1");
                }
        });

        thread1.start();
        thread2.start();
    }

为什么这2个线程都锁不住这个offer?

7c7be128d11d4408a42624aab68a8cfe.png

肥水不流外人田,既然你们都锁不住这个offer,就让我来~

public static void main(String[] args) {
        final Object resource1 = new Object();
        final Object resource2 = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("半生:锁住了机考第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("半生:笔试第一名");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("小一:锁住了笔试第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource1) {
                    System.out.println("小一:锁住机考第一名");
                }
            }
        });

        thread1.start();
        thread2.start();
    }

6187c605ae9847b1b2d39a5eccaab3af.png

又是你俩,干啥呢,死锁了呀~offer不止一个呀,就不能友好相处,快乐的玩耍同时都拿到offer么

让我来looklook一下字节码指令是怎么去执行的

1. 先使用javac -encoding UTF-8 X.java, 来生成class文件

2. javap -verbose X.class 反编译

3.从反编译的指令来看,这里应该是操作系统或者jvm虚拟机检查到了这是个死锁,强制中断了,在使用synchronized作为锁的时候,我们知道是有monitorenter monitorexit 这一对指令的,但是这里就没有看到

4. 下载了个idea的插件jclasslib 来查看,看字节码指令是否一致

5.可以看到这里是有这一对锁指令的,这里稍微解释下上写字节码指令的含义

0 aload_0:加载索引为0的引用到操作数栈,通常用于加载实例方法的隐式参数,即this。

1 dup:复制栈顶的元素,并将复制后的值重新压入栈顶。

2 astore_2:将栈顶的引用类型数值存储到局部变量表的索引为2的位置。

3 monitorenter:进入同步块前获取锁。

4 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>:获取静态字段System.out的值,即标准输出流PrintStream对象。

7 ldc #12 <小一:锁住了笔试第一名>:将常量池中索引为12的String类型常量加载到操作数栈。

9 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。

12 ldc2_w #13 <1000>:将常量池中索引为13的long类型常量加载到操作数栈。

15 invokestatic #15 <java/lang/Thread.sleep : (J)V>:执行Thread类的静态方法sleep,其中参数为栈顶的long类型常量。

18 goto 33 (+15):无条件跳转到字节码指令33,即跳过下方的指令。

21 astore_3:将栈顶的引用类型数值存储到局部变量表的索引为3的位置。

22 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>:获取静态字段System.out的值。

25 ldc #17 <小一:被中断,释放笔试资源>:将常量池中索引为17的String类型常量加载到操作数栈。

27 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。

30 aload_2:加载局部变量表中索引为2的引用类型数值到操作数栈。

31 monitorexit:退出同步块,释放锁。

32 return:返回void类型的值,并结束当前方法。

33 aload_1:加载局部变量表中索引为1的引用类型数值到操作数栈。

34 dup:复制栈顶的元素,并将复制后的值重新压入栈顶。

35 astore_3:将栈顶的引用类型数值存储到局部变量表的索引为3的位置。

这里的指令

第9 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。执行了catch中的打印日志,说明被执行中断了,后面goto 33 跳到33行的指令

6.此外我另外写了同步方法,来看看

从这里看出,它是有加锁的,第20行多了一个monitorexit,这就是防止异常强制释放锁,也就是synchronized能自动释放锁的保障

于是:解决死锁的方式来了~

退一步想阔天空,你好我好大家好

public static void main(String[] args) {
        final Object resource1 = new Object();
        final Object resource2 = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("半生:获得了机考第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("半生:获得了笔试第一名");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("小一:获得了笔试第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("小一:获得了机考第一名");
                }
            }
        });

        thread1.start();
        thread2.start();
    }

85cc84572eba4e8b85376390faadb889.png

只要稍微改下获取资源的顺序,半生跟小一就分别都获取了机考,笔试第一名,都收到了XX大厂offer

打破死锁的方式有多种,只要四个死锁的必要条件去其一就可以了

常用的有以下几种常见的方式可以用来解决死锁问题

  1. 避免循环等待:通过对资源加锁的顺序进行规定,以避免线程之间互相等待对方所持有的资源。可以通过排序或编号等方式来约定资源的获取顺序,从而避免循环等待。

  2. 破坏请求与保持条件:允许线程在请求资源时一次性获取所有需要的资源,或者在获取某个资源时释放已经占有的资源。这样可以避免一个线程持有一个资源而等待另一个资源被释放的情况。

  3. 使用资源剥夺:当一个线程请求资源时,如果资源已经被其他线程占有,则可以暂时剥夺其他线程对该资源的锁定,以满足当前线程的需求。被剥夺的线程可以等待一段时间后再重新申请资源。

  4. 使用超时机制:在获取锁资源时设置一个超时时间,在规定时间内无法获取到资源则放弃获取,释放已占有的资源,然后重新尝试。

  5. 死锁检测和恢复:通过检测系统中的死锁情况,对存在死锁的线程进行恢复或终止。常见的死锁检测算法包括资源分配图算法和银行家算法。

需要注意的是,不同的解决方式适用于不同的场景和问题,选择合适的方式需要根据具体情况进行评估。另外,预防死锁问题是更好的做法。在设计和实现时,尽量避免存在可能导致死锁的条件,从根本上杜绝死锁问题的发生。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为云云服务器评测|基于云服务器的minio部署手册

华为云云服务器评测|基于云服务器的minio部署手册 【软件安装版本】【集群安装&#xff08;是&#xff09;&#xff08;否&#xff09;】 版本 创建人 修改人 创建时间 备注 1.0 jz jz 2023.9.2 minio华为云耀服务器 一. 部署规划与架构 1. 规…

降低LLM的幻觉风险:实用策略与技术

一、前言 近年来&#xff0c;大语言模型的快速发展为构建更智能和更人性化的AI系统提供了很多可能性。像GPT-3.5、GPT-4、Bard、Claude 2和LLaMa 2等大语言模型 (LLM) 在个人助理或聊天机器人应用领域展示了强大的潜力&#xff0c;可以生成流畅而令人惊叹的响应来回答用户的问…

新学期第一篇博客

文章目录 一、加入QQ群&#xff08;一&#xff09;QQ群号&#xff08;二&#xff09;加群要求 二、加入云班课三、使用思维导图&#xff08;一&#xff09;下载XMind软件&#xff08;二&#xff09;安装XMind软件&#xff08;三&#xff09;创建思维导图1、选择模板&#xff08…

【C++】拷贝对象时,编译器的偷偷优化

你知道吗&#xff1f;对于连续的”构造拷贝构造“&#xff0c;编译器其实是会默默做出优化的。&#x1f47b; 如果你不知道这个知识点的话&#xff0c;那下面这道笔试题就要失分了&#x1f635;。 本篇分享一个关于编译器优化的小知识&#xff0c;看完本篇&#xff0c;你就能…

阿里云服务器退款规则_退款政策全解析

阿里云退款政策全解析&#xff0c;阿里云退款分为五天无理由全额退和非全额退订两种&#xff0c;阿里云百科以云服务器为例&#xff0c;阿里云服务器包年包月支持五天无理由全额退订&#xff0c;可申请无理由全额退款&#xff0c;如果是按量付费的云服务器直接释放资源即可。阿…

C++ Primer Plus 第六章笔记

目录 if 语句 if else语句 if else if else结构 逻辑运算符--&&,||和! cctype字符函数库 条件运算符&#xff08;三目运算符) switch语句 continue和break语句 基本文件输入/输出 总结&#xff1a;本文主要介绍了分支语句和if判断语句&#xff0c;运算符和简…

vue 浏览器记住密码后,自动填充账号密码错位

亲测有效&#xff01;&#xff01;! 遇到的场景&#xff1a; 浏览器记住密码后&#xff0c;登录时自动填充账号密码&#xff0c;由于登录时只需要这两个字段所以没问题&#xff0c;见图一&#xff0c;但注册时&#xff0c;账号密码不在一处&#xff0c;见图二 原本账号应该在…

浅谈JVM内存模型与GC垃圾回收

目录 1. 摘要 2. JVM 简单介绍 3. 线程私有的有哪些&#xff1f; 4. 线程共享的有哪些&#xff1f; 5. JVM 栈中程序是如何操作数据的&#xff1f; 6. 内存泄露是什么意思&#xff1f; 7. 堆内存的分配规则 8. 垃圾回收算法 8.1 垃圾回收机制简单概括 8.2 标记清理算法…

UNIAPP之js/nvue混淆探索

因项目需要对UNIAPP的js混淆做了一些调研 混淆教程: https://uniapp.dcloud.net.cn/tutorial/app-sec-confusion.html 按照教程配置进行打包正式包进行混淆 下载正式包将 .ipa改为.zip 解压获取到HBuilder.app 右键显示包内容 获取到混淆的key 不同时间进行打包混淆同一文…

谷器数据被认证为全国信标委软工分委会全权成员单位

8月23日&#xff0c;全国信息技术标准化技术委员会软件与系统工程分技术委员会&#xff08;简称&#xff1a;全国信标委软工分委会&#xff0c;SAC/TC28/SC7&#xff09;批准通过新一批成员单位&#xff0c;北京谷器数据科技有限公司被成功授牌为全权成员单位。 全国信标委软件…

高校消防安全存在的问题与解决方案介绍 安科瑞 许敏

摘要 &#xff1a;本文分析了当前高校消防安全存在的问题&#xff0c;现有消防系统突出问题表现形式&#xff0c;提出高校智慧消防安全预警防控体系构建&#xff0c;并对智慧消防安全预警平台主要功能进行了简介&#xff0c;对高校智慧消防安全预警系统功能设计具有一定的指导意…

0基础学习VR全景平台篇 第96篇:VR电子楼书

大家好&#xff0c;欢迎观看蛙色VR官方系列课程——VR电子楼书&#xff01; 作为2021年底全新上线的行业解决方案&#xff0c;是专门针对地产、园区数字化营销的一站式VR解决方案&#xff0c;为行业潜在客户提供优质的7x24小时线上看房体验。 本期教程将通过功能介绍后台操作&…

koa2快速2分钟搭建web服务器

koa简介&#xff1a; Koa是一个基于Node的web服务web服务器开发框架。类似于Express。 Koa1它的主要特点是&#xff0c;使用了ES6的Generator函数。 koa2依赖Node.js 7.6.0或者更高版本。 koa不在内核方法中绑定任何中间件&#xff0c;它仅仅是一个轻量级的函数库&#xff0c;几…

【QT】QMessageBox消息框的使用(16)

在实际项目中&#xff0c;弹出消息框是一个很常见的操作&#xff0c;包含错误信息提示、警告信息提示、关于信息提示、还包括判断信息选择等操作&#xff0c;那么今天通过这一节来好好了解下消息框的使用方法。 一.环境配置 1.python 3.7.8 可直接进入官网下载安装&#xf…

图:关键路径

1. AOE网 与AOV网不同&#xff0c;AOE是用边表示活动的图或者网。 1.AOE网的概念 在带权有向图中&#xff0c;以顶点表示事件&#xff0c;以有向边表示活动&#xff0c; 以边上的权值表示完成该活动的开销(如完成活动所需的时间)&#xff0c; 称之为用边表示活动的网络&…

如何尽快了解企业文化并融入团队

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 每个公司都有自己独特的企业文化&#xff0c;企业文化分为两个部分&#xff0c;一个部分是对外的企业文化&#xff0c;一个部分是企业内部的文化。对外的是公司的形象&…

并行计算之计算平台体系结构(流水线,异构处理器与异构并行)

并行计算粒度 分布式并行(多主机&#xff0c;多进程) &#xff1a; MPI 共享存储式并行 (多线程) &#xff1a;OpenMP OpenCL OpenACC 指令级并行 CPU流水线 CPU流水线 指将一条指令的执行过程划分为多个阶段&#xff0c;使得在每个时钟周期内能够并行执行多条指令&#…

前端开源代码

vue大屏&#xff1a; PublicbigScreenPage–Vue3tsWindcssEchartThree.js大屏案例 PublicbigScreenPage—基于 Vue3、TypeScript、DataV、ECharts 框架的 " 数据大屏项目 介绍&#xff1a;https://blog.csdn.net/qq_40282732/article/details/105656848 Vue3.2 Echar…

AutoDock Vina 对接计算(大批量)

AutoDock Vina 1.2.0 对接计算&#xff08;大批量&#xff09; AutoDockVina 1.2.0 的示例应用&#xff1a;A) 对接多个配体 (PDB 5x72)&#xff1b;B) 使用 AutoDock4 (PDB 4ykq) 的水合对接方案与水分子对接&#xff1b;C)在锌存在的情况下使用 AutoDock4Zn 力场 (PDB 1s63)&…

ChatGLM2 源码解析:`ChatGLMModel`

# 完整的 GLM 模型&#xff0c;包括嵌入层、编码器、输出层 class ChatGLMModel(ChatGLMPreTrainedModel):def __init__(self, config: ChatGLMConfig, deviceNone, empty_initTrue):super().__init__(config)# 如果设置了empty_init&#xff0c;创建任何 PyTorch 模块时&#…