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

这篇具有很好参考价值的文章主要介绍了死锁是什么?死锁的字节码指令了解?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

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

面试官:我很看好你俩,继续"干", 同时拿到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?

死锁是什么?死锁的字节码指令了解?,java知识,面试题,java,后端,面试,开发语言

肥水不流外人田,既然你们都锁不住这个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();
    }

死锁是什么?死锁的字节码指令了解?,java知识,面试题,java,后端,面试,开发语言

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

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

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

2. javap -verbose X.class 反编译

死锁是什么?死锁的字节码指令了解?,java知识,面试题,java,后端,面试,开发语言

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

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

死锁是什么?死锁的字节码指令了解?,java知识,面试题,java,后端,面试,开发语言

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.此外我另外写了同步方法,来看看

死锁是什么?死锁的字节码指令了解?,java知识,面试题,java,后端,面试,开发语言

从这里看出,它是有加锁的,第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();
    }

死锁是什么?死锁的字节码指令了解?,java知识,面试题,java,后端,面试,开发语言

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

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

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

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

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

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

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

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

需要注意的是,不同的解决方式适用于不同的场景和问题,选择合适的方式需要根据具体情况进行评估。另外,预防死锁问题是更好的做法。在设计和实现时,尽量避免存在可能导致死锁的条件,从根本上杜绝死锁问题的发生。文章来源地址https://www.toymoban.com/news/detail-702428.html

到了这里,关于死锁是什么?死锁的字节码指令了解?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 探索Java通信面试的奥秘:揭秘IO模型、选择器和网络协议,了解面试中的必备知识点!

    TCP(Transmission Control Protocol)是一种面向连接的可靠的传输协议。类似于打电话,它通过建立一个连接和保证数据的可靠传输来提高通信的可靠性。然而,由于要确保数据的可靠性,TCP协议会增加网络负担,效率相对较低。 UDP(User Datagram Protocol)是一种无连接、不可靠的传输

    2024年02月15日
    浏览(46)
  • 【字节面试】Fail-fast知识点相关知识点

    字节面试,问到的一个小知识点,这里做一下总结,其实小编之前有一篇文章,已经对此有过涉及,不过这里知识专项针对于问题,把这个知识点拎出来说一下。 什么是Fail-fast机制? Hashmap是否拥有Fail-fast机制? ConcurrentModificationException异常原因和解决方法是什么? 哪些你常

    2024年01月22日
    浏览(45)
  • 《深入理解Java虚拟机》 JAVA 字节码指令 基础

    解释时,JVM会为方法分配一个栈帧,而栈帧又由 局部变量表,操作数帧,方法引用,动态链接 组成 方法中的每条指令执行时,要求该指令的操作数已经压入栈中;执行指令时会将操作数从栈中弹出,是否将操作数再次压入栈中取决与具体的命令。 new,dup指令 使用new

    2024年02月05日
    浏览(40)
  • 《深入理解Java虚拟机》读书笔记:字节码指令简介

    字节码指令简介   Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成。由于Java虚拟机采用面向操作数栈而不是寄存器的架构(这两种架构的区别和影响将在

    2024年02月12日
    浏览(36)
  • 后端开发工程师需要了解的数据库知识

      作为一为 Java 开发工程师,写数据的查询 SQL 是必备的技能。在 日常生活中,是否统计过读数据和写数据的频率。以来开发经验来说,查询数据的操作语言是多于写数据的。   有的信息系统,数据只初始化一次,甚至是服务一辈子。   接触过很多的 web 开发系统,都是为

    2024年02月08日
    浏览(52)
  • java八股文面试[数据库]——MySQL死锁的原因和处理方法

    1) 表的死锁 产生原因 : 用户A访问表A(锁住了表A),然后 又访问表B ;另一个用户B访问表B(锁住了表B),然后企图 访问表A ;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。 用户A--》A表(表

    2024年02月09日
    浏览(47)
  • 云计算第1阶段_Linxu基础知识_day03,字节跳动8年老Linux运维面试官经验谈

    5 远程连接sshd服务 ps:sshd默认端口号:22 5.1 手动修改默认端口号 6 文件拷贝、移动、删除 6.1 拷贝文件或目录 6.2 移动文件或目录 **ps:**判断上一条命令是否执行成功 命令:echo $? 如果返回值为0,证明我们上一条命令执行是成功的, 如果返回值非0,证明我们上一条命令执行

    2024年04月12日
    浏览(42)
  • 字节跳动大厂面试题详解:java中有哪些类型的锁

    作者简介 :一名后端开发人员,每天分享后端开发以及人工智能相关技术,行业前沿信息,面试宝典。 座右铭 :未来是不可确定的,慢慢来是最快的。 个人主页 :极客李华-CSDN博客 合作方式 :私聊+ 这个专栏内容 :BAT等大厂常见后端java开发面试题详细讲解,更新数目10

    2024年02月21日
    浏览(46)
  • 深入理解JVM虚拟机第二十七篇:详解JVM当中InvokeDynamic字节码指令,Java是动态类型语言么?

     😉😉 学习交流群: ✅✅1:这是孙哥suns给大家的福利! ✨✨ 2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 🥭🥭3:QQ群: 583783824   📚📚  工作微信: BigTreeJava 拉你进微信群,免费领取! 🍎🍎4:本文章内容出自上述:Spring应用课程!💞💞

    2024年02月04日
    浏览(44)
  • JAVA面试部分——后端-线程前篇

    3.1 线程和进程 在计算机科学中,进程和线程是操作系统管理资源的两种不同方式。 进程(Process) :是程序在计算机上的一次执行活动。每个进程都有自己的内存空间,包括代码、数据和系统资源。一个进程可以包含多个线程。进程之间相互独立,各自拥有独立的内存空间

    2024年01月20日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包