java死锁的成因和解决方案

一、什么是死锁? 

在Java中,死锁是指两个或多个线程互相持有对方所需要的锁,并且在无法继续执行的情况下永久地等待对方释放锁。这种情况下,所有涉及的线程都无法继续执行,程序被卡住,无法正常终止。

死锁通常发生在多线程并发执行时,当线程之间相互竞争获取资源的时候。典型的死锁场景包括以下四个条件的同时满足:

  1. 互斥:至少有一个资源同时只能被一个线程占用;
  2. 占有并等待:一个线程已经占有一个资源,同时还等待另一个线程占有的资源;
  3. 不可剥夺:一个线程已经占有的资源不能被其他线程强制性剥夺;
  4. 环路等待:多个线程之间形成等待资源的环路。

当这四个条件同时满足时,就会导致死锁的发生。为了避免死锁,可以采取一些预防措施,例如合理地设计资源分配顺序、避免线程持有多个锁、使用定时锁等待等方法来减少死锁的风险。

 

二、产生死锁的三个典型场景 

1、一个线程一把锁

如果一个线程对同一把锁,连续加了两次锁,就会产生死锁。当然如果锁是可重入锁则不会死锁,可重入锁,字面意思是“可以重新进入的锁”,即允许同一个线程多次获取同一把锁,不会出现死锁的情况。(每次线程拿锁的时候,都会判断一下这个锁的已拥有者有没有这个线程,如果有则会放行,因此就不会出现一个线程连续加了两次锁,也就不会死锁)

解决方案:

无脑使用synchronized,因为它是可重入锁。 

2、两个线程两把锁

public class demo2 {public static void main(String[] args) {Object locker1 = new Object();Object locker2 = new Object();    Thread t1 = new Thread(() -> { synchronized (locker1) {synchronized (locker2) {}}});t1.start();Thread t2 = new Thread(() -> {synchronized (locker2) {synchronized (locker1) {}}});t2.start();}

死锁原因:线程t1拿到锁1,线程t2拿到锁2。接着线程t1想要锁2,但此时锁2被线程t2占用着,t1无法获取,陷入阻塞等待,而t2想要锁1,但锁1被t1占用着。就这样t1和t2陷入僵局,谁也无法正常释放锁,从而形成了死锁。

解决办法

线程t1与线程t2的加锁顺序一样即可:

public class demo2 {public static void main(String[] args) {Object locker1 = new Object();Object locker2 = new Object();    Thread t1 = new Thread(() -> { synchronized (locker1) {synchronized (locker2) {}}});t1.start();Thread t2 = new Thread(() -> {synchronized (locker1) {synchronized (locker2) {}}});t2.start();}

3、N个线程M把锁 

哲学家问题:

哲学家问题是计算机科学领域中的一个经典问题,它用来探讨在并发编程中可能出现的资源竞争和死锁问题。这个问题通常用于说明多线程和并发编程中的困难和挑战。

问题的设定是这样的:有一圈圆桌上有一些哲学家,每个哲学家面前放有一个盘子,圆桌上有一些餐叉,每两个相邻的哲学家之间有一把餐叉。哲学家的生活有两种状态:思考和进餐。当哲学家思考时,他们不需要任何资源,但当他们饿了的时候,他们会试图拿起左右两边的餐叉,如果两把餐叉都可用,他们就可以进餐,进餐完毕后,他们会放下餐叉继续思考。

问题的关键在于如何协调哲学家之间的行为,以避免死锁(所有哲学家都拿起一只餐叉,等待另一只,导致无法继续进餐)和资源竞争(多个哲学家同时试图拿起同一把餐叉,导致冲突)。

解决这个问题的方法包括使用各种同步机制,如互斥锁、信号量等,以确保哲学家在进餐时能够安全地占用需要的资源,避免发生死锁和资源竞争。

哲学家问题在并发编程中是一个经典的例子,用来说明如何有效地管理共享资源,以防止出现一些常见的并发问题。

 解决办法

这个问题有很多方法的,其中一个方法是我们规定其中一个人等其他人都吃完了才可以拿筷子。按照这样的思路,这个死锁问题就可以得到解决。

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

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

相关文章

代立冬:基于Apache Doris+SeaTunnel 实现多源实时数据仓库解决方案探索实践

大家好,我是白鲸开源的联合创始人代立冬,同时担任 Apache DolphinScheduler 的 PMC chair 和 SeaTunnel 的 PMC。作为 Apache Foundation 的成员和孵化器导师,我积极参与推动多个开源项目的发展,帮助它们通过孵化器成长为 Apache …

【GEE】时间序列多源遥感数据随机森林回归预测|反演|验证|散点图|完整代码

实验介绍 分类和回归之间的主要区别在于,在分类中,我们的预测目标是离散的类别,而在回归中,预测目标是连续的预测值。 本实验的研究区域位于佛蒙特州的埃塞克斯郡,使用训练数据来模拟土壤氧化还原深度,然…

中缀表达式转后缀表达式与后缀表达式计算(详解)

**中缀表达式转后缀表达式的一般步骤如下: 1:创建一个空的栈和一个空的输出列表。 2:从左到右扫描中缀表达式的每个字符。 3:如果当前字符是操作数,则直接将其加入到输出列表中。 4:如果当前字符是运算符&a…

告别新手!超详细深色UI设计指南

深色用户界面的设计,从手中握持的移动屏幕到宽广的大型屏幕,都已经越来越普遍。深色UI具有何种独特魅力呢?它所带来的不仅仅是力量和奢华的象征,更是精致优雅的体现。然而,将一个设计从浅色主题调整到深色主题&#xf…

docker安装node及使用

文章目录 一、安装node二、创建node容器三、进入创建的容器如有启发,可点赞收藏哟~ 一、安装node 查看可用版本 docker search node安装最新版本 docker install node:latest二、创建node容器 docker run -itd --name node-test node–name node-test&#xff1…

Spark---DataFrame存储、Spark UDF函数、UDAF函数

四、DataFrame存储Spark UDF函数 1、储存DataFrame 1)、将DataFrame存储为parquet文件 2)、将DataFrame存储到JDBC数据库 3)、将DataFrame存储到Hive表 2、UDF:用户自定义函数 可以自定义类实现UDFX接口 java: …

SpringMVC 案例

文章目录 前言1. 计算器1.1 准备前端代码1.2 测试前端代码1.3 完成后端代码1.4 验证程序 2. 留言板2.1 前端代码准备2.2 测试前端代码2.3 完成前后端交互代码2.4 完成后端代码2.5 案例测试2.6 完善前后端交互2.7 完善后端代码2.8 完整功能测试 lombok简单的方式添加Lombok工具3…

RHEL8_Linux访问NFS存储及自动挂载

本章主要介绍NFS客户端的使用 创建FNS服务器并通过NFS共享一个目录在客户端上访问NFS共享的目录自动挂载的配置和使用 1.访问NFS存储 前面介绍了本地存储,本章就来介绍如何使用网络上的存储设备。NFS即网络文件系统,所实现的是 Linux 和 Linux 之间的共…

线程池的使用及实现

使用多进程进行并发编程,会频繁的创建销毁进程,效率比较慢,所以引入了线程,线程使用复用资源的方式提高了创建销毁的效率,但是随着创建线程的频率进一步提高,开销仍然无法忽略不计了。 要想办法优化此处线…

亚信安慧AntDB数据库中级培训ACP上线,中国移动总部首批客户认证通过

近日,亚信安慧AntDB数据库ACP(AntDB Certified Professional)中级培训课程于官网上线。在中国移动总部客户运维团队、现场项目部伙伴和AntDB数据库成员的协同组织下,首批中级认证学员顺利完成相关课程的培训,并获得Ant…

一键抠图3:Android实现人像抠图 (Portrait Matting)

一键抠图3:Android实现人像抠图 (Portrait Matting) 目录 一键抠图3:Android实现人像抠图 (Portrait Matting) 1. 前言 2. 抠图算法 3. 模型Android部署 (1) 将Pytorch模型转换ONNX模型 (2) 将ONNX模…

前端学习微信小程序开发

1.微信小程序项目结构 2.WXML和HTML的区别 3.WXSS与CSS的区别 4.小程序中的.js文件 5.小程序的宿主环境 宿主环境是指程序运行所必须的依赖环境,因此手机微信时小程序的宿主环境。小程序宿主环境包含了通信模型、运行机制、组件、API。 (1)…