Java 死锁及避免讲解和案例示范

在大型分布式系统中,死锁是一种常见但难以排查的并发问题。特别是在 Java 领域,死锁问题可能导致系统崩溃或卡顿。本文将以电商交易系统为例,详细讲解如何识别和避免 Java 程序中的死锁问题,确保系统高效运行。

1. 什么是死锁?

死锁是指多个线程相互持有对方所需的资源,且都在等待对方释放资源的状态。此时,这些线程将永远无法继续执行,造成系统资源被占用,无法释放。

在 Java 中,死锁主要发生在 synchronized 块或锁(如 ReentrantLock)的使用中。当两个或多个线程在请求多个共享资源时,如果请求的顺序不一致,就可能导致死锁。

1.1 类图说明

死锁通常发生在多线程环境下,其中线程竞争同一个或多个资源,导致互相等待,进入“僵局”状态。我们可以通过一个简单的类图来展示两个线程和两个锁的相互依赖关系。

死锁类图

在这里插入图片描述

类图解释:
  • Thread1 线程持有 Lock1,并等待 Lock2
  • Thread2 线程持有 Lock2,并等待 Lock1
  • 由于两个线程相互等待对方持有的锁,形成了死锁。

1.2 时序图说明

时序图可以更直观地展示死锁发生的过程。在下图中,两个线程分别尝试获取两把锁,结果导致相互等待。

死锁时序图

在这里插入图片描述

时序图解释:
  • Thread1 首先获取 Lock1,然后尝试获取 Lock2,但此时 Lock2Thread2 占用。
  • Thread2 获取了 Lock2,但在尝试获取 Lock1 时,发现 Lock1 已经被 Thread1 占用。
  • 两个线程彼此等待对方释放锁,导致了死锁。

2. 如何检测死锁?

2.1 使用 JDK 自带的工具

2.1.1 jstack 工具

jstack 是 JDK 自带的工具,用于查看 Java 虚拟机中的线程堆栈。通过它,可以检查当前 JVM 中所有线程的状态,包括是否存在死锁。我们可以使用 jstack 来捕获线程状态,从中判断是否存在死锁。

使用步骤

  1. 首先,找到正在运行的 JVM 进程 ID(PID)。可以使用

    jps
    

    命令获取:

    jps
    
  2. 然后使用

    jstack
    

    工具查看线程堆栈:

    jstack <PID>
    

在输出的堆栈信息中,若某个线程显示为 waiting to lock,并且有多个线程都处于类似状态,则可能存在死锁问题。通过线程堆栈中的资源锁信息,可以定位到具体的死锁代码。

示例:

假设我们有一个电商系统中的库存和订单两个模块:

public class DeadlockExample {private final Object lock1 = new Object();private final Object lock2 = new Object();public void method1() {synchronized (lock1) {System.out.println("Thread 1: Holding lock 1...");try { Thread.sleep(100); } catch (InterruptedException e) {}synchronized (lock2) {System.out.println("Thread 1: Holding lock 2...");}}}public void method2() {synchronized (lock2) {System.out.println("Thread 2: Holding lock 2...");try { Thread.sleep(100); } catch (InterruptedException e) {}synchronized (lock1) {System.out.println("Thread 2: Holding lock 1...");}}}public static void main(String[] args) {DeadlockExample example = new DeadlockExample();Thread t1 = new Thread(example::method1);Thread t2 = new Thread(example::method2);t1.start();t2.start();}
}

在上述代码中,method1method2 中的锁请求顺序不同,可能导致死锁。执行 jstack 工具可以帮助我们确认这一死锁问题。

2.1.2 jconsole 工具

jconsole 是一个基于 GUI 的监控工具,能够直观地展示线程的运行状态。它提供了“线程”选项,可以查看每个线程的运行状况,并能检测到死锁。

使用步骤

  1. 运行 jconsole,选择相应的 JVM 进程连接。
  2. 在工具中选择“线程”选项卡,查看线程堆栈及是否存在死锁。

2.2 使用代码检测

2.2.1 ThreadMXBean

Java 提供了 ThreadMXBean 类来检测死锁。ThreadMXBean 可以检测到 JVM 中的死锁线程,代码如下:

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;public class DeadlockDetector {private static final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();public static void detectDeadlock() {long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();if (deadlockedThreads != null) {ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads);for (ThreadInfo threadInfo : threadInfos) {System.out.println("Deadlock detected:");System.out.println(threadInfo);}} else {System.out.println("No deadlock detected.");}}public static void main(String[] args) {// Create threads that may lead to deadlocknew Thread(new DeadlockExample()::method1).start();new Thread(new DeadlockExample()::method2).start();// Check for deadlockdetectDeadlock();}
}

通过调用 findDeadlockedThreads 方法,可以检测当前 JVM 中是否有死锁线程,并打印相关信息。

3. Java 中的死锁原因

3.1 资源竞争

死锁的本质是资源竞争。线程 A 持有资源 1,并尝试请求资源 2;与此同时,线程 B 持有资源 2,并尝试请求资源 1。如果双方都不释放各自持有的资源,就会发生死锁。

在电商系统中,常见的场景包括库存锁定和订单生成,两个模块需要同时操作数据库中的库存和订单表。如果不合理设计锁的顺序,就有可能发生死锁。

3.2 锁的嵌套

在多线程环境中,嵌套锁的使用是死锁的常见原因。当一个线程在持有一个锁的情况下,又尝试请求另一个锁时,容易导致死锁。

示例:
public void updateInventoryAndOrder() {synchronized (inventoryLock) {synchronized (orderLock) {// 更新库存和订单}}
}

如果两个线程分别在不同的方法中请求相反的锁,就可能出现死锁。

4. 如何避免死锁?

4.1 尽量减少锁的使用

最简单的避免死锁的方法就是减少锁的使用,或者尽量不要对多个资源进行嵌套锁定。

4.2 遵循锁顺序

确保所有线程在获取多个锁时,遵循相同的顺序。通过锁顺序的一致性,可以有效避免死锁。

示例:
public void safeMethod() {synchronized (lock1) {synchronized (lock2) {// 执行操作}}
}

在所有需要使用 lock1lock2 的地方,都按照相同的顺序请求锁,从而避免死锁的发生。

4.3 使用 tryLock 方法

Java 中的 ReentrantLock 提供了 tryLock() 方法,可以尝试获取锁,如果获取失败,则不会进入等待状态,从而避免死锁。

示例:
public void safeTryLockMethod() {if (lock1.tryLock()) {try {if (lock2.tryLock()) {try {// 执行操作} finally {lock2.unlock();}}} finally {lock1.unlock();}}
}

tryLock 提供了更为灵活的锁控制机制,避免线程因获取不到锁而一直等待,进而导致死锁。

5. 电商系统中的死锁示范与避免

在电商交易系统中,库存模块和订单模块的并发操作是高频场景。如果不合理设计锁的获取方式,容易导致死锁。

示例:死锁情境

假设我们有一个电商系统,其中库存和订单分别对应两个锁。

public class EcommerceSystem {private final Object inventoryLock = new Object();private final Object orderLock = new Object();public void updateInventory() {synchronized (inventoryLock) {System.out.println("Holding inventory lock...");try { Thread.sleep(100); } catch (InterruptedException e) {}synchronized (orderLock) {System.out.println("Holding order lock...");}}}public void updateOrder() {synchronized (orderLock) {System.out.println("Holding order lock...");try { Thread.sleep(100); } catch (InterruptedException e) {}synchronized (inventoryLock) {System.out.println("Holding inventory lock...");}}}
}

在这种情况下,两个线程同时调用 updateInventoryupdateOrder 时,可能会出现死锁。

示例:避免死锁

可以通过一致的锁获取顺序来避免死锁:

public class EcommerceSystem {private final Object inventoryLock = new Object();private final Object orderLock = new Object();public void updateInventoryAndOrder() {synchronized (inventoryLock) {synchronized (orderLock) {System.out.println("Holding both inventory and order lock...");// 执行更新操作}}}
}

在所有操作中,先获取 inventoryLock,再获取 orderLock,确保锁的顺序一致,避免死锁的发生。

6. 总结

Java 死锁是并发编程中的常见问题,尤其是在涉及到多个共享资源时。本文通过电商交易系统的实际场景,介绍了死锁的定义、检测方法以及避免策略。通过合理的锁设计、遵循锁顺序、使用 tryLock 等机制,能够有效地避免 Java 程序中的死锁问题,提高系统的稳定性和并发处理能力。

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

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

相关文章

Cesium 路径导航线

Cesium 路径导航线 const points [-115.05079620730267, 37.19786588187864,-115.22324534761827, 37.543541965531986,-115.47419222019222, 37.615105164973855,-115.48787694581283, 37.40861889557443,-115.41627083519458, 37.11794056537019,-115.1758997349438, 36.951…

C0007.Clion中添加ui文件及运行的完整步骤

1.创建ui文件 选择Ui文件目录,右击,打开Qt Designer; 创建完成后,保存ui界面,并且命名为test.ui; 2.新建头文件test.h 在include目录中,新建头文件,文件名为test.h 3.新建test.cpp源文件

Web安全 - 重放攻击(Replay Attack)

文章目录 OWASP 2023 TOP 10导图1. 概述2. 重放攻击的原理攻击步骤 3. 常见的重放攻击场景4. 防御重放攻击的技术措施4.1 使用时效性验证&#xff08;Time-Based Tokens&#xff09;4.2 单次令牌机制&#xff08;Nonce&#xff09;4.3 TLS/SSL 协议4.4 HMAC&#xff08;哈希消息…

YOLOv11尝鲜测试五分钟极简配置

ultralytics团队在最近又推出了YOLOv11&#xff0c;不知道在有生之年能不能看到YOLOv100呢哈哈。 根据官方文档&#xff0c;在 Python>3.8并且PyTorch>1.8的环境下即可安装YOLOv11&#xff0c;因此之前YOLOv8的环境是可以直接用的。 安装YOLOv11&#xff1a; pip instal…

DevExpress WinForms v24.1新版亮点:富文本编辑器、电子表格组件功能升级

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…

VMware 虚拟机 下载安装 Centos7 和Windows10 镜像源

准备工作 下载 VMware链接&#xff1a;稍后发布链接 Centos7完整版链接&#xff1a;https://www.123865.com/ps/EF7OTd-mdAnH Centos7mini版链接&#xff1a;https://www.123865.com/ps/EF7OTd-1dAnH Windows10链接&#xff1a;https://www.123865.com/ps/EF7OTd-4dAnH 演示环境…

Linux Shell编程快速入门以及案例(Linux一键批量启动、停止、重启Jar包Shell脚本)

文章目录 1&#xff0c;shell核心概念1. 1&#xff0c;变量1. 2. 引号1. 3. 循环1. 4. 条件判断1. 5. 函数1. 6. 重定向1. 7. 管道1. 8. 通配符 2&#xff0c;案例 本文通过介绍Linux Shell编程中最重要的十个核心概念&#xff0c;帮助初学者快速入门。 1&#xff0c;shell核心…

pdf怎么盖章不覆盖文字

pdf怎么盖章不覆盖文字&#xff1f;在PDF上盖章而不覆盖文字&#xff0c;可以按照以下步骤操作&#xff1a; 使用福昕PDF编辑器 下载安装&#xff1a;从福昕软件官网下载并安装福昕PDF编辑器。打开文档&#xff1a;用福昕PDF编辑器打开需要盖章的PDF文档。访问图章工具&#…

Innodb磁盘结构

系统表空间 系统表空间的作用 存系统表中的数据存数据字典————保存系统变量和状态变量存变更缓冲区中未来得及落盘的数据 系统表空间文件位置 系统表可以对应磁盘上的多个表空间文件&#xff0c;默认情况下服务器在数据目录下创建一个名为ibdata1的文件 可以通过改变系…

帝都程序猿十二时辰

前言 2019年度国产剧《长安十二时辰》火了&#xff0c;其口碑榜首、节奏紧凑、贴合原著、电影质感&#xff0c;都是这部剧的亮点。而最令人震撼的还是剧中对大唐盛世的还原&#xff0c;长安街坊的市容市貌、长安百姓的生活日常、长安风情的美轮美奂……而关于十二时辰的话题也接…

媒界:蓝山城市NOA再开5城,第二次OTA开启推送智能化实力全面提升

9月30日&#xff0c;魏牌全新蓝山的城市NOA再次取得重大进展&#xff0c;其智驾开城版图进一步扩张至石家庄、武汉、广州、郑州、西安五座重要城市。继保定、成都、重庆、深圳四城开通之后&#xff0c;全新蓝山的城市NOA智驶网络实现了又一次跨越式增长&#xff0c;这不仅展现了…

D21【python接口自动化学习】-python基础之内置数据类型

day21 内置数据类型文档使用 学习日期&#xff1a;20240928 学习目标&#xff1a;内置数据类型--30 内置数据类型参考&#xff1a;如何使用官方文档与帮助&#xff1f; 学习笔记&#xff1a; 使用官方文档 可通过终端查看数据类型的方法 使用帮助 总结 官方文档是体系化的…

<使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理>

<使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理> 文章目录 <使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理>1.冒泡排序实现形式总结1.1关于冒泡排序实现形式1的来源&#xff1a;1.2对四种排序实现形式使用AI进行无引导分析&#xff1a;1.3AI&…

【floor报错注入】

一、sql语句基础 floor 向下取整 count 取数据的数量 group by 分组查询 Rand 随机数 limit 二、floor报错注入 主键重复报错 我们先了解group by产生的虚拟表的原理&#xff0c;了解到虚拟表的主键是不可以重复的 我们再可以通过Rand(0)函数规定固定种子后乘2&…

Win10之Ubuntu22.04(主机)与Virtual-BOX(宿主win10)网络互通调试(七十九)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

An End-to-End Local Attention Based Model for Table Recognition(ICDAR 2023)

An End-to-End Local Attention Based Model for Table Recognition(ICDAR 2023) 一.前述 作者认为基于Transformer的表格识别模型很难处理大表格的识别&#xff0c;原因是受限于它的全局注意力global attention机制。 基于以上&#xff0c;作者提出了一种局部注意力local a…

.NET Core 高性能并发编程

一、高性能大并发架构设计 .NET Core 是一个高性能、可扩展的开发框架&#xff0c;可以用于构建各种类型的应用程序&#xff0c;包括高性能大并发应用程序。为了设计和开发高性能大并发 .NET Core 应用程序&#xff0c;需要考虑以下几个方面&#xff1a; 1. 异步编程 异步编程…

在线css像素Px到百分比(%)换算器

具体请前往&#xff1a;在线Px转百分比(%)工具--将绝对像素(px)长度单位转换为相对父级元素内尺寸的相对长度单位百分比(%)

PCL 点云模型滤波(圆形)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 生成点云数据 2.1.2 模型滤波函数 2.1.3 可视化函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xf…

树和二叉树知识点大全及相关题目练习【数据结构】

树和二叉树 要注意树和二叉树是两个完全不同的结构、概念&#xff0c;它们之间不存在包含之类的关系 树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个结点的有限集&#xff0c;它或为空树&#xff08;n 0&#xff09;&#xff1b;或为非空树&a…