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

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

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

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

面试官:我很看好你俩,继续"干", 同时拿到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.mzph.cn/news/69092.shtml

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

相关文章

win10 ping不通 Docker ip(解决截图)

背景&#xff1a; win10下载了docker desktop就是这个图&#xff0c;然后计划做一个springboot连接docker。 docker部署springboot :docker 部署springboot(成功、截图)_總鑽風的博客-CSDN博客 问题&#xff1a;spring boot部署docker后&#xff0c;docker接口通了&#xff0…

Java之文件操作与IO

目录 一.认识文件 1.1文件是什么&#xff1f; 1.2文件的组织 1.3文件路径 1.4文件的分类 二.文件操作 2.1File概述 三.文件内容操作--IO 3.1JavaIO的认识 3.2Reader和Writer ⭐Reader类 ⭐Writer类 3.2FileInputStream和FileOutputStream ⭐FileInputStream类 …

Weblogic SSRF【漏洞复现】

文章目录 漏洞测试注入HTTP头&#xff0c;利用Redis反弹shell redis不能启动问题解决 Path : vulhub/weblogic/ssrf 编译及启动测试环境 docker compose up -dWeblogic中存在一个SSRF漏洞&#xff0c;利用该漏洞可以发送任意HTTP请求&#xff0c;进而攻击内网中redis、fastcgi…

DDR2 IP核调式记录2

本文相对简单&#xff0c;只供自己看看就行。从其它的博客找了个代码&#xff0c;然后记录下仿真波形。 1. 功能 直接使用quartus生成的DDR2 IP核&#xff0c;然后实现循环 -->写入burst长度的数据后读出。 代码数据的传输是32位&#xff0c;实际使用了两片IC。因此IP核也是…

8月《中国数据库行业分析报告》已发布,聚焦数据仓库、首发【全球数据仓库产业图谱】

为了帮助大家及时了解中国数据库行业发展现状、梳理当前数据库市场环境和产品生态等情况&#xff0c;从2022年4月起&#xff0c;墨天轮社区行业分析研究团队出品将持续每月为大家推出最新《中国数据库行业分析报告》&#xff0c;持续传播数据技术知识、努力促进技术创新与行业生…

【分布式搜索引擎es】

文章目录 数据搜索DSL实现查询文档搜索结果处理 RestClient实现 旅游案例酒店搜索和分页酒店结果过滤我周边的酒店酒店竞价排名 elasticsearch最擅长的是 搜索和 数据分析。 数据搜索 DSL实现 查询文档 常见的查询类型包括&#xff1a; 查询所有&#xff1a;查询出所有数…

读SQL学习指南(第3版)笔记11_字符串函数和数值函数

1. 尽管SQL标准指定了部分函数&#xff0c;但数据库厂商并没有遵循这些函数规范 2. 字符串 2.1. char 2.1.1. 固定长度、不足部分用空格填充的字符串 2.1.2. MySQL允许的char类型的最大长度为255个字符 2.1.3. Oracle Database允许的最大长度为2,000个字符 2.1.4. SQL Se…

Java 面试 - Redis

Redis Redis 是基于键值对的非关系型数据库。Redis 拥有string、hash、list、set、zset等多种数据结构, redis具有惊人的读写性能, 其优秀的持久化机制是的它在断电和机械故障时也不会发生数据丢失, 可以用于热点数据存放, 还提供了键过期、发布订阅、食物、流水线、LUA脚本等多…

按钮控件的基类--- QAbstractButton 类(抽象类)

1、QAbstractButton 属性 QAbstractButton 属性速查表属性名说明属性名说明autoExclusive自动排他性checked是否被选中autoRepeat是否启用自动重复down是否处于按下状态autoRepeatDelay初始延迟(毫秒)icon按钮上显示的图标autoRepeatInterval时间间隔(毫秒iconSize显示的图标的…

Vue + Element UI 前端篇(十二):用户管理模块

Vue Element UI 实现权限管理系统 前端篇&#xff08;十二&#xff09;&#xff1a;用户管理模块 用户管理模块 添加接口 在 http/moduls/user.js 中添加用户管理相关接口。 import axios from ../axios/* * 用户管理模块*/// 保存 export const save (params) > {ret…

windows查看端口占用,通过端口找进程号(查找进程号),通过进程号定位应用名(查找应用)(netstat、tasklist)

文章目录 通过端口号查看进程号netstat通过进程号定位应用程序tasklist 通过端口号查看进程号netstat 在Windows系统中&#xff0c;可以使用 netstat 命令来查看端口的占用情况。以下是具体的步骤&#xff1a; 打开命令提示符&#xff08;CMD&#xff09;&#xff1a;按WinR组…

Linux简介

为什么选择Linux&#xff1f; Linux是一个优秀的操作系统 硬件方面&#xff1a;适合嵌入式&#xff0c;服务器&#xff0c;移动设备&#xff0c;桌面&#xff0c;计算机集群和超级计算机应用方面&#xff1a;人工智能&#xff0c;分布式计算&#xff0c;云计算&#xff0c;大数…

电气工程中重要的测量术语:“kVRMS” | 百能云芯

在电气工程和电子领域&#xff0c;术语“kVRMS”至关重要。它是工程师和技术人员用来准确评估电气系统电压的关键测量方法。在这篇综合文章中&#xff0c;我们将深入探讨 kVRMS 的含义、其意义、应用。 kVRMS 代表“千伏均方根”。为了理解这个术语&#xff0c;我们来分解一下&…

Linux的服务器日志分析及性能调优

作为网络安全和数据传输的重要环节&#xff0c;代理服务器在现代互联网中扮演着至关重要的角色。然而&#xff0c;在高负载情况下&#xff0c;代理服务器可能面临性能瓶颈和效率问题。本文将介绍如何利用Linux系统对代理服务器进行日志分析&#xff0c;并提供一些实用技巧来优化…

rpm打包

文章目录 rpm打包 1. rpm打包步骤0&#xff09;准备工作&#xff1a;安装打包工具rpm-build和rpmdevtools&#xff08;1&#xff09;在线安装&#xff08;2&#xff09;离线安装 1&#xff09;创建初始化目录2&#xff09;准备打包内容3&#xff09;编写打包脚本 spec文件4&…

测试需求分析

什么是软件测试需求&#xff1a; 灰度测试&#xff1a;先发布部分功能&#xff0c;然后看用户的反馈&#xff0c;再去发布另外一部分的更新 A/B测试&#xff1a;先发布的功能先让A部分的用户进行更新&#xff0c;再根据用户的犯困再更新B用户的功能 需求测试&#xff1a; 功…

hive 基础知识

一 hive 是什么 在本节前我们需要明确 hive 是什么 上面两个代码块&#xff0c;左边的是 mapreduce 的代码块&#xff0c;右边的是hive 的代码块 很容易看出来&#xff0c;右边的 hive 写起来要更容易更快些&#xff0c;而执行效率&#xff0c;右边的 hive 只比左边多一个翻译…

Redis 7 第六讲 主从模式(replica)架构篇

🌹🌹🌹 此篇开始进入架构篇范围(❤艸`❤) 理论 即主从复制,master以写为主,Slave以读为主。当master数据变化的时候,自动将新的数据异步同步到其它slave数据库。 使用场景 读写分离 容灾备份数据备份水平扩容主从架构 演示案例 注:masterauth、replicaof主…

直线导轨的替换方法

目前&#xff0c;直线导轨的使用率持续上升&#xff0c;已广泛应用在各种各样的行业中&#xff0c;可替换性高是其广泛使用的重要原因之一&#xff01;直线导轨的替换指的就是导轨和滑块可以单出&#xff0c;不用整套替换。 市面上使用率最高的直线导轨品牌应该就是台湾*银了&a…

优先级队列priority_queue以及仿函数的使用

目录 优先级队列priority_queuepriority_queue的模拟实现仿函数 优先级队列priority_queue 优先级队列priority_queue是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它默认第一个元素总是它所包含的元素中最大的 优先级队列默认使用vector作为底层存储数据的…