【Java】产生死锁的必要条件和如何避免死锁

news2025/2/26 8:33:08

首先我们先简单了解一下什么是死锁

我们模拟A,B是两个资源,而下面是两个要抢占资源的任务
在这里插入图片描述
首先左边的任务执行,抢占了A的锁资源
在这里插入图片描述
当他想拿继续执行任务,拿B的锁资源的时候,B的资源被右边的任务抢走了
在这里插入图片描述
这时候我们应该要了解一下产生死锁的四个必要条件:

四个必要条件

  1. 一个资源每次只能被一个线程使用
  2. 一个线程在阻塞等待某资源时,不释放已占有资源
  3. 一个线程已经获得的资源,在未使用完之前,不能被强行剥夺
  4. 若干线程形成头尾相接的循环等待资源关系

由这四个条件可知
首先,左右任务各自抢占了一个资源,以左为例,首先会想到继续拿资源B,但是根据上面的条件1,3可以知道,资源不能多路复用,也不能强行剥夺
这时,以人的思维来讲,应该退让一步,把A资源也给让出去,但是由于条件2可以得知,线程并不会这么做,他拿不到资源B就会一直把资源A握在手里,陷入了进退两难的地步,而右边的任务同样面临着这样的境地
最后,他们等待抢夺资源形成了一个闭环,这个闭环不一定是两个,也可能是多个,如
在这里插入图片描述
他们构成了头尾相接的循环等待,满足了第四个条件,这样一个标准的死锁就诞生哩!

死锁是一个开发的事故,他不能解决,只能尽可能的去避免

如何避免死锁:

其实很简答,造成死锁必须要满足上面的四个条件,而我们只要在开发过程中,破坏掉任意一个条件,就可以避免死锁

而其中前三个条件作为锁要符合的条件,所以避免死锁就需要打破第四个条件,不出现循环等待的锁的关系

在开发过程中:

  1. 需要注意加锁顺序,保证每个线程按照同样的顺序进行加锁
    如:
public class DeadlockExample {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: Holding lock1...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 1: Waiting for lock2...");
                synchronized (lock2) {
                    System.out.println("Thread 1: Acquired lock2.");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 2: Holding lock1...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 2: Waiting for lock2...");
                synchronized (lock2) {
                    System.out.println("Thread 2: Acquired lock2.");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

两个线程都按照先锁lock1,再锁lock2的顺序执行,就可以避免陷入循环

  1. 要注意加锁时限,可以针对锁设置一个超时时间
public class LockTimeoutExample {
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        try {
            if (lock.tryLock(5, TimeUnit.SECONDS)) {
                try {
                    System.out.println("Acquired the lock.");
                    // Perform your critical section work here
                } finally {
                    lock.unlock();
                    System.out.println("Released the lock.");
                }
            } else {
                System.out.println("Could not acquire the lock within the timeout.");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在上面的例子中,我们使用了ReentrantLock作为锁,并在tryLock()方法中设置了超时时间为5秒。如果在5秒内成功获取了锁,则可以执行临界区代码,然后在结束后释放锁。如果在5秒内无法获取锁,就会执行相应的超时操作。
比如我抢到了A,在抢B时设置一个时间,如果超过了这个时间就不抢B了,然后顺手也给A放了,这样也能很好的避免死锁

  1. 要注意死锁检查,这是一种预防机制,确保第一时间发现死锁并且解决
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DeadlockDetectionExample {
    private static final Lock lock1 = new ReentrantLock();
    private static final Lock lock2 = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: Holding lock1...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 1: Waiting for lock2...");
                synchronized (lock2) {
                    System.out.println("Thread 1: Acquired lock2.");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock2...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 2: Waiting for lock1...");
                synchronized (lock1) {
                    System.out.println("Thread 2: Acquired lock1.");
                }
            }
        });

        thread1.start();
        thread2.start();

        Thread.sleep(2000); 
        
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        long[] deadlockedThreadIds = threadMXBean.findDeadlockedThreads();
        if (deadlockedThreadIds != null) {
            System.out.println("Deadlock detected. Taking corrective action...");

           
            ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreadIds);
            for (ThreadInfo threadInfo : threadInfos) {
                System.out.println("Thread ID: " + threadInfo.getThreadId());
                System.out.println("Thread Name: " + threadInfo.getThreadName());
                System.out.println("Thread State: " + threadInfo.getThreadState());
                System.out.println("Blocked Time: " + threadInfo.getBlockedTime());
                System.out.println("Lock Name: " + threadInfo.getLockName());
                System.out.println("Lock Owner ID: " + threadInfo.getLockOwnerId());
                System.out.println("Lock Owner Name: " + threadInfo.getLockOwnerName());
                System.out.println();
            }

       
            for (long threadId : deadlockedThreadIds) {
                Thread thread = threadMXBean.getThreadInfo(threadId).getThread();
                if (thread != null) {
                    thread.interrupt(); 
                }
            }
        }
    }
}

在这个案例中,我们创建了两个线程,并故意设计了一个死锁情况。然后,我们使用ThreadMXBean来检测死锁。如果死锁被检测到,我们打印出有关死锁线程的信息,并采取措施来解决死锁,如中断线程。

需要注意的是,死锁检测和解决可能会涉及到复杂的逻辑和操作,具体的处理方法取决于实际情况。在实际应用中,你可能需要根据检测到的死锁情况来采取不同的措施,以确保系统的稳定性和可靠性。

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

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

相关文章

OCP China Day 2023:五大社区齐聚,加速开源开放创新与落地

8月10日,2023年开放计算中国社区技术峰会(OCP China Day 2023)在北京举行。智慧时代,计算多元化、应用多样化、技术复杂化正驱动数据中心新一轮变革,开源开放社区已成为推动数据中心持续创新的重要力量,通过…

YOLOv5、YOLOv8改进:添加ShuffleAttention注意力机制

广泛应用的注意力机制主要有空间注意力机制和通道注意力机制,其目的分别是捕捉像素级的成对关系和通道依赖关系。虽然将两种机制融合在一起可以获得比单独更好的性能,但计算开销不可避免。因而,本文提出Shuffle Attetion,即SA&…

力扣:59. 螺旋矩阵 II(Python3)

题目: 给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 来源:力扣(LeetCode) 链接:力扣(LeetCode)官网 - 全…

【1572. 矩阵对角线元素的和】

来源:力扣(LeetCode) 描述: 给你一个正方形矩阵 mat,请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1: 输入:mat [[1,2,3]…

AtCoder Beginner Contest 313D题题解

文章目录 [ Odd or Even](https://atcoder.jp/contests/abc313/tasks/abc313_d)问题建模问题分析1.分析每次查询的作用2.利用异或运算的性质设计查询方法 Odd or Even 问题建模 有n个数,每个数为0或者1,最多可以进行n次询问,每次询问选择k个…

第04天 Spring是如何解决循环依赖的

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏:每天一个知识点 ✨特色专栏&#xff1a…

推出稳定代码:人工智能辅助编码的新视野

推荐:使用 NSDT场景编辑器 快速助你搭建可二次编辑的3D应用场景 在不断发展的软件开发环境中,对效率和可访问性的追求导致了各种工具和平台的创建。最新的创新之一是StableCode,这是Stability AI的大型语言模型(LLM)生…

Flv格式视频怎么转MP4?视频格式转换方法分享

FLV格式的视频是一种早期的视频格式,不支持更高的分辨率和比特率,这意味着视频的清晰度和质量受限制,无法很好地保留细节和质量,这种格式的视频已经逐渐被更高质量的视频格式所替代,例如MP4格式,不仅具有很…

创新不辍,再结硕果 | 蓝奥声“无线联动监控技术”

随着无线电通信技术的迅速发展,无线远程监控系统也得到了技术上的更新,它将嵌入式产品与现代无线通信技术相结合,共同构成了一种新型的监测控制系统。物联网及其相关无线联动通信技术是智能科技快速发展的重要支撑技术之一,由此带…

主流国产GPU产品及规格概述(2023)

​ 美国对 AI 芯片出口管制,自主可控要求下国产芯片需求迫切。2022 年 10 月 7 日美国商务部工业安全局(BIS)发布《美国商务部对中华人民共和国(PRC)关于先进计算和半导体实施新的出口管制制造》细则,其中管…

复古游戏库管理器RomM

什么是 RomM ? RomM(代表 Rom Manager)是一个专注于复古游戏的游戏库管理器。通过 Web 浏览器管理和组织您的所有游戏。受 Jellyfin 的启发,允许您从现代界面管理所有游戏,同时使用 IGDB 元数据丰富它们。 RomM 支持的…

电脑自动重启是什么原因?这几个原因不可忽视!

“感觉我的电脑也没有用多久呀,怎么总是会出现自动重启的情况呢?由于我对电脑不是很熟悉,都不知道该如何解决这个问题,有没有朋友可以解释一下这是为什么呀?“ 在使用电脑时,如果电脑总是自动重启&#xff…

MySQL_索引的使用与设计

最左前缀法则 最左前缀法则适用于联合索引;查询从索引的最左列开始,不跳过其中的列,如果跳过其中的列将会导致索引失效(后面字段的索引失效)。 验证最左前缀法则 三个列的联合索引都同时使用 explain select * from u…

基于R做宏基因组的进化树ClusterTree分析

写在前面 同上一篇的PCoA分析,这个也是基于公司结果基础上的再次分析,重新挑选样本,在公司结果提供的csv结果表上进行删减,本地重新分析作图 步骤 表格预处理 在公司给的ClusterTree的原始表格数据里选取要保留的样本&#xf…

腾讯云轻量应用服务器CPU配置?主频性能

腾讯云轻量应用服务器CPU型号是什么?处理器主频多少?轻量应用服务器不支持指定CPU处理器型号,目前腾讯云服务器网账号下的轻量应用服务器,CPU采用2.5GHz主频的Intel(R) Xeon(R) Gold 6133 处理器,睿频 3.0GHz&#xff…

CTFSHOW php命令执行

目录 web29 过滤flag web30 过滤system php web31 过滤 cat|sort|shell|\. 这里有一个新姿势 可以学习一下 web32 过滤 ; . web33 web34 web35 web36 web37 data伪协议 web38 短开表达式 web39 web40 __FILE__命令的扩展 web41 web42 重定向…

对话即数据分析,网易数帆ChatBI做到了

大数据产业创新服务媒体 ——聚焦数据 改变商业 在当今数字化快速发展的时代,数据已经成为业务经营与管理决策的核心驱要素。无论是跨国大企业还是新兴创业公司,正确、迅速地洞察数据已经变得至关重要。然而,传统的BI工具往往对用户有一定的…

YOLOv5、YOLOv8改进:SEAttention 通道注意力机制

基于通道的注意力机制 源自于 CVPR2018: Squeeze-and-Excitation Networks 官方代码:GitHub - hujie-frank/SENet: Squeeze-and-Excitation Networks 如图所示,其实就是将不同的通道赋予相关的权重。Attention机制用到这里用朴素的话说就是,…

【论文阅读】基于深度学习的时序预测——FEDformer

系列文章链接 论文一:2020 Informer:长时序数据预测 论文二:2021 Autoformer:长序列数据预测 论文三:2022 FEDformer:长序列数据预测 论文地址:https://arxiv.org/abs/2201.12740 github地址&a…

Cobbler自定义yum源

再次了解下Cobbler的目录结构: 在/var/www/cobbler/ks_mirror目录下存放的是所有的镜像。 存放的是仓库镜像: 在/var/lib/cobbler/kickstarts目录下是存放的所有的kickstarts文件。 再有就是/etc/cobbler这个目录: [rootvm1 loaders]# cd /…