【Java EE】-多线程编程(四) 死锁
作者:学Java的冬瓜
博客主页:☀冬瓜的主页🌙
专栏:【JavaEE】
分享:2023.3.31号骑行的照片再发一次(狗头)。
主要内容:什么是死锁?不可重入可重入、死锁的三个典型情况:1、一个线程一把锁,连续加锁两次 2、两个线程两把锁,同时获取对方的锁 3、N个线程M把锁,哲学家就餐问题。死锁的四个必要条件,如何破除死锁。

文章目录
- 一、什么是死锁?
- 二、不可重入(Java中可重入)
- 三、死锁的3个典型情况
- 1、一个线程一把锁,连续加锁两次
- 2、两个线程两把锁,同时获取对方的锁
- 3、N个线程M把锁
- 四、死锁的4个必要条件
- 1、互斥
- 2、不可抢占
- 3、请求和保持
- 4、循环等待
- 五、如何破除死锁
- 1、破除情况1
- 2、破除情况2、3
一、什么是死锁?
- 死锁是指两个或者两个以上的进程(线程)在执行过程中,因争夺资源而造成一种互相等待的现象。这些进程(线程)在等待彼此释放已经占用的资源,它们都无法继续执行下去。死锁的产生可能使系统进入假死状态,无法响应任何输入。
二、不可重入(Java中可重入)
- 一个线程在执行一个临界区时,需要访问一个共享资源,例如一个共享变量或函数。但是,如果在该临界区内部再次请求访问该共享资源,则可能会导致死锁。因为此时该共享资源已经被该线程占用,无法再次获得访问权,而其他线程也无法访问该资源,从而产生死锁。
如下操作,锁对象都是this,注意:修饰方法就是给当前的实例对象加锁(也就是this对象)
执行进入add方法时,这个线程给this第一次加锁,可以加锁成功,然后遇到代码块,尝试再次加锁。但是呢,在this看来,自己已经被第一个线程加过锁了,所以此时尝试加锁的线程就会阻塞等待,但其实两次加锁的线程是同一个,这就是不可重入导致死锁。
synchronized public void add() {synchronized(this) {count++;}
}
如果两次加锁的线程不是同一个,那不可重入,只是让后一个加锁的线程阻塞等待,不会死锁。但是同一个线程对同一个对象加两次锁,就会死锁。为了防止这种死锁,在java中,synchronized、ReentrantLock加锁时,我们给线程开了个后门,让线程可重入。就是说:同一个线程给同一个对象加锁时,可以反复加锁。(但是在c++,python,操作系统原生的锁是不可重入的)。
那么怎么实现呢?我可以在这个锁对象上记录下来,当前的锁是属于哪个线程的,如果再次加锁的线程是记录在所对象中的线程,就直接放过去,不阻塞,(两次加锁时,锁对象是同一个)。如果不是同一个线程,那就让后加锁的线程阻塞等待。
三、死锁的3个典型情况
1、一个线程一把锁,连续加锁两次
在上面已经说过,在java中,synchronized河ReentrantLock都是可重入的,所以一个线程一把锁,连续加锁两次,在java中是不会死锁的,但是其它语言中就不一样了比如Python,C++,操作系统原生的锁都是不可重入的,都会造成死锁。
synchronized public void add() {synchronized(this) {count++;}
}
2、两个线程两把锁,同时获取对方的锁
在下面代码中给了一个例子:线程一有钱,要拿去换货,而线程2有货,拿货去换钱。但是在交换时,线程1不肯先把钱给线程2,而线程二也不肯先把货交给线程一,这就让两个线程无法继续往下执行,导致死锁。
// 死锁演示:开始时,线程1拿到钱,线程2拿到货,但是交换时谁都不肯先给对方,所以就死锁了
public class Main {public static void main(String[] args) {Object qian = new Object();Object huo = new Object();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (qian){// sleep为了确保线程2先拿到货,而不是由线程1拿到钱和货try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (huo){System.out.println("线程1拿到了钱和货");}}}});Thread t2 = new Thread(){@Overridepublic void run(){synchronized (huo){// sleep为了确保线程1先拿到钱,而不是由线程2先拿到货和钱try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (qian){System.out.println("线程2拿到了货和钱");}}}};t1.start();t2.start();try {// 确保t1和t2在main之前执行t1.join();t2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
3、N个线程M把锁
典型例子:哲学家就餐问题
问题描述:
五个哲学家围坐在一张圆桌前,每个哲学家面前都有一碗米饭和一只筷子。哲学家只会做两件事情:思考和就餐。当哲学家想要就餐时,他必须同时拿到他左右两边的筷子。如果他只拿到了一只筷子,他就不能就餐。当哲学家就餐时,他会占用两只筷子,其他哲学家就不能就餐了。每个哲学家思考的时间是随机的,就餐的时间也是随机的。当所有哲学家都就餐完毕后,程序结束。
实现方式:
为了避免死锁问题,可以让其中一个哲学家先拿右边的筷子,而其他哲学家则先拿左边的筷子,这样就可以避免所有哲学家同时拿起左边的筷子,而导致死锁问题的发生。其实也就是给筷子编号,每个哲学家只能先拿小的筷子,如果不理解可以看以下图示:

四、死锁的4个必要条件
1、互斥
线程1拿到锁,线程2就得阻塞等待。
2、不可抢占
线程1拿到锁,线程2就得阻塞等待,等线程1释放锁后,线程2才能尝试获取锁。
3、请求和保持
线程1拿到锁 A后,再尝试获取 B,A这把锁还是保持的,不会因为要获取 B就把 A释放了。
4、循环等待
线程1有锁 A,尝试获取锁 B。线程2有锁 B,尝试获取锁 A。即线程1和线程2都在等待对方释放拥有的锁,然后尝试自己获取到。
五、如何破除死锁
1、破除情况1
在Java中,synchronized和ReentrantLock都是可重入锁,同一个线程对同一个对象加两次锁,不会死锁。所以,不需要考虑。至于其它语言,以后学到了再说。
2、破除情况2、3
方法:给对象编号,按照顺序,加锁和释放锁。
比如情况2中,给钱编号为1,给货编号为2,按编号加锁。两个线程获取时,只能都先获取钱的锁,当前程1获取到钱的锁时,线程2只能等待线程1释放钱的锁,而线程1可以获取到货的锁了,等它都获取到锁后执行完后,把货和钱的锁都释放了。那此时线程2就可以尝试获取钱的锁,获取到后,再去尝试获取货的锁。
在代码上来看的话,两个线程都应该是外面是给qian加锁,里面是给huo加锁,如下示例。
Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (qian){// sleep为了确保线程2先拿到货,而不是由线程1拿到钱和货try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (huo){System.out.println("线程1拿到了钱和货");}}}});
在情况3中,我们可以给每个筷子编号,每个哲学家只能先拿编号小的筷子,那这样的话,即使是极端情况下也不会出现死锁,具体情况见下图。

相关文章:
【Java EE】-多线程编程(四) 死锁
作者:学Java的冬瓜 博客主页:☀冬瓜的主页🌙 专栏:【JavaEE】 分享:2023.3.31号骑行的照片再发一次(狗头)。 主要内容:什么是死锁?不可重入可重入、死锁的三个典型情况:1、一个线程一…...
学习数据结构第1天(数据结构的基本概念)
数据结构的基本概念基本概念和术语数据结构的三要素经典试题基本概念和术语 1.数据 数据是信息的载体,是描述客观事物属性的数、字符以及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据是计算机程序加工的原料。 2.数据元素 数据元素是数据的基本…...
南大通用数据库-Gbase-8a-学习-33-空洞率查询与解决方法
目录 一、个人理解 二、存储过程 三、虚机测试 四、解决方法 1、重建表 2、shrink space 一、个人理解 空洞率的产生是由于delete语句并不会真实的删除数据,只是在数据上打了一个不可见标签,但实际还是占用着相应的存储空间。 二、存储过程 自定义…...
为什么我们认为GPT是一个技术爆炸
从23年初,ChatGPT火遍全球,通过其高拟人化的回答模式,大幅提升了人机对话的体验和效率,让用户拥有了一个拥有海量知识的虚拟助手,根据UBS发布的研究报告显示,ChatGPT在1月份的月活跃用户数已达1亿ÿ…...
程序员如何能提高自己的编程水平?
这些实用的小建议,能帮你迅速地提高编程水平: 不要做无意义的奋斗 拒绝喊口号和无意义的奋斗,包括但不限于: ①做了计划表却从未有执行的一天; ②每天都是最早来、最晚走,但是工作进度趋近于0;…...
从零使用vuepress搭建个人博客部署.github.io
前言 记录小白如何搭建个人博客 github部署的博客👉: DreamLuffe的博客 netilify部署的博客:👉:DreamLuffe的博客 个人博客搭建实战 网上有很多优秀的开源博客页面,我们就直接安装好,再继续…...
Python 进阶指南(编程轻松进阶):十一、注释、文档字符串和类型提示
原文:http://inventwithpython.com/beyond/chapter11.html 源代码中的注释和文档可能和代码一样重要。原因是软件是永远不会完成的;无论是添加新功能还是修复错误,您总是需要做出改变。但是你不能改变代码,除非你理解它࿰…...
python item()方法
Python中有很多方法来解决一些简单的问题,其中最常见的就是用 item ()方法来完成。item ()方法的全称是item-process (),该方法用来对对象进行创建、删除、改变、添加、更新等操作。…...
【day2】Android Jetpack Compose环境搭建
【day2】Android Jetpack Compose环境搭建 以下是适用于 Jetpack Compose 的环境要求: Android Studio 版本:4.2 Canary 15 或更高版本Gradle 版本:7.0.0-beta02 或更高版本Android 插件版本:4.2.0-beta15 或更高版本Kotlin 版本…...
stable-diffusion安装和简单测试
参考: https://github.com/CompVis/stable-diffusion 理解DALLE 2, Stable Diffusion和 Midjourney的工作原理 Latent Diffusion Models论文解读 【生成式AI】淺談圖像生成模型 Diffusion Model 原理 【生成式AI】Stable Diffusion、DALL-E、Imagen 背後…...
MATLAB算法实战应用案例精讲-【智能优化算法】 基于帕累托包络的选择算法II(PESA-II)(附MATLAB代码实现)
目录 前言 知识储备 数据包络分析(DEA) 特点 名词解释 类型介绍 案例简介 软件操作(SPSSPRO)...
【华为机试真题详解JAVA实现】—坐标移动
目录 一、题目描述 二、解题代码 一、题目描述 开发一个坐标计算工具, A表示向左移动,D表示向右移动,W表示向上移动,S表示向下移动。从(0,0)点开始移动,从输入字符串里面读取一些坐标,并将最终输入结果输出到输出文件里面。 输入: 合法坐标为A(或者D或者W或者S) +…...
【软考五】数据库(做题)
该文章不适合学习数据库,适合考证,遇到实际问题的,不要在这儿浪费时间。切记切记 软考之数据库一、概念数据模型(下午题常考)二、结构数据模型关系模型1、关系模型中基本术语2、关系模型中的关系完整性约束3、关系代数…...
【Java Web】012 -- SpringBootWeb综合案例(登录功能、登录校验、异常处理)
目录 一、登录功能 1、基础登录功能 ①、SQL语句 ②、接口参数 ③、实现思路 ④、实现步骤 2、联调Bug(没有Cookie或Session) 二、登录校验 1、登录校验的实现思路 2、会话技术 ①、会话与会话跟踪 ②、会话跟踪方案对比 Cookie Session …...
跨界智能手表:比亚迪向左,小鹏向右
如今,电动化、智能化是汽车行业转型的大方向,而由于目前国内汽车产业在电动化方面已经算是“小有成效”,因此,抢占智能化高地,打造一个多设备互融的生态系统,就成为了车企的共同愿景。在此背景下࿰…...
【c++初阶】第九篇:vector(常用接口的使用 + 模拟实现)
文章目录vector介绍vector的使用vector的定义vector iterator(迭代器) 的使用begin和endrbegin和rendvector 空间增长问题size和capacityreserve和resize(重点)测试vector的默认扩容机制emptyvector的增删查改push_back和pop_backinsert和erasefindswapo…...
Taro React组件使用(6) —— RuiSendCode 短信验证码【倒计时】
1. 需求分析 获取验证码按钮,点击后进入倒计时环节;默认采用 120s 后才允许再次发送短信验证码;发送后不能再次点击发送按钮,点击也不执行发送逻辑;最好将发送短信的业务逻辑请求接口写在组件中,封装为公用组件,可以多处使用。2. 实现效果 2.1 验证码发送前 2.2 验证码…...
把ChatGPT接入我的个人网站
效果图 详细内容和使用说明可以查看我的个人网站文章 把ChatGPT接入我的个人网站 献给有外网服务器的小伙伴 如果你本人已经有一台外网的服务器,并且页拥有一个OpenAI API Key,那么下面就可以参照我的教程来搭建一个自己的ChatGPT。 需要的环境 Cento…...
关于数字游民是未来年轻人工作趋势的一种思考
Q:我觉得未来,数字游民会是中国工作的一种主流方式,因为实体行业受到严重冲击,科技的发展是推导支持这样的远程工作形式,而且未来人的时间是越来越离散化、碎片化、原子化的,以订单交付的形式,P2P的形式会是…...
2022年 合肥市经开区信息学竞赛区赛 初中组
2022年 合肥市经开区信息学竞赛区赛 初中组T1.普通排序 题目描述 牛牛是一位编程爱好者,今天第一次参加初中组比赛,看到第一题,不要紧张,来一个简单的排序题做一做,牛牛学过了很多排序,一直想练个手,这回机会来了,给牛牛N个数(n<=100),每个数都在(0 ~ 1000)之间…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...
