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

news2024/12/24 11:29:16

在大型分布式系统中,死锁是一种常见但难以排查的并发问题。特别是在 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 deadlock
        new Thread(new DeadlockExample()::method1).start();
        new Thread(new DeadlockExample()::method2).start();

        // Check for deadlock
        detectDeadlock();
    }
}

通过调用 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.coloradmin.cn/o/2181487.html

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

相关文章

如何初步部署自己的服务器,达到生信分析的及格线2(待更新)

参考我的上一篇博客https://blog.csdn.net/weixin_62528784/article/details/142621762?spm1001.2014.3001.5501&#xff0c; 现在我们已经有了一个能够跑一些基础任务的、基本没有配置的服务器了&#xff0c;接下来要做的任务就是&#xff1a; &#xff08;1&#xff09;进一…

centos一些常用命令

文章目录 查看磁盘信息使用 df 命令使用 du 命令 查看磁盘信息 使用 df 命令 df&#xff08;disk free&#xff09;命令用于显示文件系统的磁盘空间占用情况。 查看所有挂载点的磁盘使用情况&#xff1a; df -h选项说明&#xff1a; -h 参数表示以人类可读的格式&#xff0…

开发微信小程序 基础02

WX模板 1.对比 ①标签名称不同 ②属性节点不同 ③提供类似vue的模板语法 2.模板语法 2.1数据动态绑定 2.1.1在data种定义数据 在页面对应的.js文件中&#xff0c;把数据定义到data对象中即可 例---data &#xff1a; { info : init data , msList : [{msg : hello}, { ms…

开发微信小程序 基础03

WXSS(类似CSS) 定义&#xff1a; WXSS (WeiXin Style Sheets)是一套样式语言&#xff0c;用于描述 WXML的组件样式&#xff0c;类似于网页开发中的 CSS。 分类&#xff1a; 全局样式&#xff1a;定义在 app.wxss 中的样式为全局样式&#xff0c;作用于每一个页面 局部样式&…

解决 Android WebView 无法加载 H5 页面常见问题的实用指南

目录 1. WebView 简介 2. 常见问题 3. 网络权限设置 4. 启用 JavaScript 5. DOM Storage 的重要性 6. 处理 HTTPS 问题 7. 设置 WebViewClient 8. 调试工具 9. 其他调试技巧 10. 结论 相关推荐 1. WebView 简介 Android WebView 是一种视图组件&#xff0c;使得 And…

基于SSM+小程序的电影院订票选座管理系统(电影2)(源码+sql脚本+视频导入教程+文档)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM的电影院订票选座小程序管理系统实现了管理员和用户二个角色。管理员实现了用户管理、影院信息管理、电影类型管理、电影信息管理、系统管理、订单管理等。用户实现了影院信息、电…

【论文笔记】Flamingo: a Visual Language Model for Few-Shot Learning

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Flamingo: a Visual Langu…

16.安卓逆向-frida基础-HOOK类方法2

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

链表的基础知识

文章目录 概要整体架构流程 小结 概要 链表是一种常见的数据结构&#xff0c;它通过节点之间的连接关系实现数据的存储和访问。链表由一系列节点&#xff08;Node&#xff09;组成&#xff0c;每个节点包含数据和指向下一个节点的指针。链表的特点是物理存储单元上非连续、非顺…

《基于多视角深度学习技术的乳腺X线分类:图网络与Transformer架构的研究》|文献速递-基于多模态-半监督深度学习的病理学诊断与病灶分割

Title 题目 Mammography classification with multi-view deep learning techniques:Investigating graph and transformer-based architectures 《基于多视角深度学习技术的乳腺X线分类&#xff1a;图网络与Transformer架构的研究》 01 文献速递介绍 乳腺X线摄影是乳腺癌…

鸿蒙开发(NEXT/API 12)【请求用户授权】手机侧应用开发

为保护用户隐私&#xff0c;Wear Engine的API需要用户授权才可以正常访问。建议开发者在用户首次调用Wear Engine开放能力的时候执行本章节操作。 申请用户穿戴设备权限 应用拉起华为账号登录和授权界面&#xff0c;由用户授权相应的数据访问权限。用户可以自主选择授权的数据…

828华为云征文|华为云Flexus云服务器X实例——uniapp功能开发、搭建股票系统选择用什么服务器比较好?

在二次开发、安装搭建股票系统时&#xff0c;选择华为云Flexus X服务器是一个值得考虑的优质选项。以下是一些具体的建议&#xff1a; 测试环境&#xff1a;Linux系统CentOS7.6、宝塔、PHP7.3、MySQL5.7&#xff0c;根目录public&#xff0c;伪静态thinkphp&#xff0c;开启ssl…

1、深入理解Redis线程模型

文章目录 一、Redis是什么&#xff1f;有什么用&#xff1f;1、Redis是什么&#xff1f;2、2024年的Redis是什么样的&#xff1f; 二、Redis到底是单线程还是多线程&#xff1f;三、Redis如何保证指令原子性1、复合指令2、Redis事务3、Pipeline4、lua脚本5、Redis Function6、R…

CTFshow信息搜集web1~web20详解

目录 1、web1 源码泄露 2、web2 页面源代码泄露 3、 web3 响应头泄露 4、web4 robots协议 5、web5 phps源码泄露 6、web6 源码压缩包泄露 7、web7 GIT泄露 8、web8 SVN泄露 9、web9 vim缓存 10、web10 cookie 11、web11 域名解析 12、web12 网站公开信息 13、web13 技…

python4_画方格

python4_画方格 import turtledef cell():# 画第一个方格# 设置画笔宽度为1turtle.width(1)# 下笔,这样&#xff0c;路径就会画出来turtle.pendown()# 前进30px像素turtle.forward(30)# 设置为黑色turtle.color("black")# 方向转90度turtle.left(90)# 前进30px像素t…

HTML+CSS基础 第二季课堂笔记

一、列表 列表都不是单打独斗的&#xff0c;通常都是一组标签组成 1 无序列表 作用&#xff1a;定义一个没有顺序的列表结构 由两个标签组成&#xff0c;ul&#xff08;容器级标签&#xff09;&#xff0c;li&#xff08;容器级&#xff09; ul&#xff1a;英文ulordered …

828华为云征文 | 华为云Flexus云服务器X实例搭建企业内部VPN私有隧道,以实现安全远程办公

VPN虚拟专用网络适用于企业内部人员流动频繁和远程办公的情况&#xff0c;出差员工或在家办公的员工利用当地ISP就可以和企业的VPN网关建立私有的隧道连接。 通过拨入当地的ISP进入Internet再连接企业的VPN网关&#xff0c;在用户和VPN网关之间建立一个安全的“隧道”&#xff…

探索顶级低代码开发平台,实现创新

文章盘点ZohoCreator、OutSystems等10款顶尖低代码开发平台&#xff0c;各平台以快速开发、集成、数据安全等为主要特点&#xff0c;适用于不同企业需求&#xff0c;助力数字化转型。 一、Zoho Creator Zoho Creator 是一个低代码开发平台&#xff0c;它简化了应用开发中的复杂…

解决MySQL命令行中出现乱码问题

在MySQL命令行中遇到乱码问题通常是由于字符编码设置不正确导致的。以下是一些解决步骤&#xff1a; 1. **检查和设置字符集**&#xff1a; 首先&#xff0c;您需要确保MySQL服务器、客户端和数据库使用的是正确的字符集。您可以通过执行以下命令来查看当前的字符集设置&…

领英(LinkedIn)高效开发国外客户的6个技巧

社媒开发客户大家现在用的都挺多&#xff0c;每个社媒平台都有自己的特点&#xff0c;领英&#xff08;LinkedIn&#xff09;因为他特殊的职场定位&#xff0c;这上面有非常多的大客户&#xff0c;适合做B端的外贸企业&#xff0c;今天就来给大家分享一下如何利用领英高效开发国…