产生死锁的四个必要条件

news2024/11/19 7:49:36

产生死锁的四个必要条件

  1. 互斥使用: 一个资源每次只能被一个线程使用。这意味着如果一个线程已经获取了某个资源(比如锁),那么其他线程就必须等待,直到该线程释放资源。

  2. 不可抢占: 已经获得资源的线程在释放资源之前,不能被其他线程抢占。只有拥有资源的线程自己能够释放资源,其他线程无法将其强行抢占。

  3. 请求保持: 一个线程在持有至少一个资源的情况下,又请求获取其他资源。这样的情况下,如果其他资源被其他线程持有并且不释放,就会导致请求线程等待,从而可能形成死锁。

  4. 循环等待: 存在一组等待进程 {P1, P2, ..., Pn},其中P1等待P2持有的资源,P2等待P3持有的资源,...,Pn等待P1持有的资源。这样的循环等待条件是死锁的充分条件

注意 以上是必要条件 在数学中 分必要条件 充要条件等 必要条件是四个缺一不可的 

也就是说 要产生死锁 这些条件是不可或缺的

以上四个必要条件分别用java代码解释说明

目录

互斥使用

不可抢占

请求保持

循环等待


互斥使用

互斥是指在多任务处理中,对共享资源的访问进行限制,确保同一时刻只有一个任务(或线程)能够访问共享资源。这种限制保证了对共享资源的安全访问,避免了数据竞争和数据不一致的问题。

在并发编程中,互斥通常通过锁(如Java中的`synchronized`关键字或`Lock`接口)来实现。当一个任务需要访问共享资源时,它会尝试获取锁,如果锁已被其他任务持有,则该任务会被阻塞,直到锁被释放。一旦任务获取到锁,它就可以安全地访问共享资源,在完成操作后释放锁,以便其他任务可以继续访问。

函数起名根据: 

public static void criticalSection1() {
            System.out.println(Thread.currentThread().getName() + "进入临界区");
            System.out.println(Thread.currentThread().getName() + "离开临界区");
    }

    public static void main(String[] args) {
        new Thread(()->{
            criticalSection1();
        }).start();
        new Thread(()->{
            criticalSection1();
        }).start();
    }

对于这段代码 会有这样一个执行结果 因为是并发执行的

可以看到 在进程1进入临界区的时候 0也能进入临界区

接下来我们加上一段锁

private static final Object lock = new Object();

    public static void criticalSection() {
        synchronized(lock) {
            System.out.println(Thread.currentThread().getName() + "进入临界区");
            // 这里是临界区,只有一个线程可以执行这段代码
            System.out.println(Thread.currentThread().getName() + "离开临界区");
        }
    }

    public static void main(String[] args) {
        new Thread(()->{
            criticalSection();
        }).start();
        new Thread(()->{
                    criticalSection();
                }).start();
    }

对于这段代码  只有这一一种执行结果 因为每次只有一个线程能够进入临界区执行代码,确保了临界区内的操作不会被并发执行,从而避免了数据竞争和数据不一致的问题。 表现了互斥等到(两个或多个线程同时想要获取一个资源 但是只能等到另一个释放)

不可抢占

public class Main3 {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        // 线程1获取锁并执行耗时操作
        new Thread(() -> {
            try {
                // 等待2启动,保证2在1之后获取锁
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized(lock) {
                System.out.println("线程1获得了锁");
                try {
                    // 模拟耗时操作
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1释放了锁");
            }
        }).start();

        // 线程2尝试获取锁
        new Thread(() -> {
            try {
                // 等待一段时间,模拟线程2稍晚启动
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized(lock) {
                // 线程2无法获取锁,因为锁已被线程1持有
                System.out.println("线程2获得了锁");
            }
            System.out.println("线程2释放了锁");
        }).start();
    }
}

以上代码有两种执行结果

线程2获得了锁
线程2释放了锁
线程1获得了锁
线程1释放了锁
线程1获得了锁
线程1释放了锁
线程2获得了锁
线程2释放了锁

而没有 1获得了锁下一句是2释放了锁 这种情况

这就表现不可抢占的特性,即已经获得资源的线程在释放资源之前,不能被其他线程抢占。

请求保持

先想一下这段话:  一个线程在持有至少一个资源的情况下,又请求获取其他资源。这样的情况下,如果其他资源被其他线程持有并且不释放,就会导致请求线程等待,从而可能形成死锁。

我们可以理解为 

t1线程 在持有lock资源的情况下,又请求获取lock2资源。这样的情况下,如果lock2资源被t2线程持有并且不释放,就会导致请求线程等待,从而可能形成死锁。

public class Main4 {
    private static Object lock = new Object();
    private static Object lock2 = new Object();
    public static void main(String[] args) {
        Thread t1 = new Thread(()-> {
            synchronized (lock) {
                System.out.println("t1获得lock");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (lock2) {
                    System.out.println("t1获得lock2");
                }
            }
        });
        Thread t2 = new Thread(()->{
            synchronized (lock2) {
                System.out.println("t2获得lock2");
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }
}

可以观察到 t2不释放lock2 t1就不能拿到lock2

以上表现出 请求保持是死锁的一个必要条件之一,指的是一个线程在持有至少一个资源的情况下,又请求获取其他资源,但这些资源已被其他线程持有并且不释放,从而导致请求线程等待,可能形成死锁。

循环等待

也就是哲学家进餐问题

public class Main5 {
    private static final Object resource1 = new Object();
    private static final Object resource2 = new Object();

    public static void main(String[] args) {
        // 线程1持有资源1,请求资源2
        Thread t1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("线程1持有资源1");
                try {
                    Thread.sleep(1000); // 为了确保线程2先持有资源2
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resource2) {
                    System.out.println("线程1持有资源2");
                }
            }
        });

        // 线程2持有资源2,请求资源1
        Thread t2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("线程2持有资源2");
                synchronized (resource1) {
                    System.out.println("线程2持有资源1");
                }
            }
        });

        t1.start();
        t2.start();
    }
}

导致了循环等待 也就是 存在一组等待进程 {P1, P2},其中P1等待P2持有的资源,P2等待P1持有的资源

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

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

相关文章

[leetcode] all-nodes-distance-k-in-binary-tree 二叉树中所有距离为 K 的结点

. - 力扣(LeetCode) 给定一个二叉树(具有根结点 root), 一个目标结点 target ,和一个整数值 k 。 返回到目标结点 target 距离为 k 的所有结点的值的列表。 答案可以以 任何顺序 返回。 示例 1&#xff1a…

一文了解ERC404协议

一、ERC404基础讲解 1、什么是ERC404协议 ERC404协议是一种实验性的、混合的ERC20/ERC721实现的,具有原生流动性和碎片化的协议。即该协议可让NFT像代币一样进行拆分交易。是一个图币的互换协议。具有原生流动性和碎片化的协议。 这意味着通过 ERC404 协议&#xf…

键值数据库Redis——Windows环境下载安装+命令行基本操作+Java操纵Redis

文章目录 前言一、下载与安装(Windows环境)** 检查数据库连接状态 **** 查看Redis数据库信息 ** 二、Redis五种数据结构与基本操作获取所有的key——keys *清空所有的key——flushall2.1 字符串操作2.2 散列操作2.3 列表操作2.4 集合操作2.5 位图操作 三…

【Java EE】 IoC详解(Bean的存储)

文章目录 🎍Controller(控制器存储)🌸如何从Spring容器中获取对象(ApplicationContext)🌸获取bean对象的其他方式(BeanFactory)🌸Bean 命名约定🌸…

Java调用http接口的几种方式(HttpURLConnection、OKHttp、HttpClient、RestTemplate)

Java作为后端语言是开发接口实现功能供客户端调用接口,这些客户端中最主要是本项目的前端;但有时候也需要Java请求其他的接口,比如需要长连接转短链接(请求百度的一个接口可以实现)、获取三方OSS签名、微信小程序签名、…

数据结构(六)----串

目录 1.串的定义 2.串的基本操作 3.串的存储结构 (1)串的定义 •顺序存储 •链式存储 (2)求串长 (3)求子串 (4)比较串的大小 (5)定位操作 4.字符串的模式匹配 (1)朴素模式匹配算法 (2)KMP算法 •求模式串中的next数组(重点) •练习&#…

第四百六十回

文章目录 1. 概念介绍2. 方法与细节2.1 获取方法2.2 使用细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取当前系统语言"相关的内容,本章回中将介绍如何获取时间戳.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章…

适配器模式:连接不兼容接口的桥梁

在软件开发中,适配器模式是一种结构型设计模式,它允许不兼容的接口之间进行交互,从而使它们能够一起工作。这个模式经常用于系统升级或集成第三方库的时候,当现有的代码无法直接使用新系统或库提供的接口时,适配器可以…

基于Java+Vue的中国咖啡文化宣传网站(源码+文档+包运行)

一.系统概述 本课题是根据咖啡文化宣传需要以及网络的优势建立的一个中国咖啡文化宣传网站,来实现中国咖啡文化宣传以及咖啡商品售卖的功能。 本中国咖啡文化宣传网站应用Java技术,MYSQL数据库存储数据,基于SSMVue框架开发。在网站的整个开发…

【QT入门】Qt自定义控件与样式设计之自定义QLineEdit实现搜索编辑框

往期回顾 【QT入门】Qt自定义控件与样式设计之qss的加载方式-CSDN博客 【QT入门】Qt自定义控件与样式设计之控件提升与自定义控件-CSDN博客 【QT入门】Qt自定义控件与样式设计之鼠标相对、绝对位置、窗口位置、控件位置-CSDN博客 【QT入门】Qt自定义控件与样式设计之自定义QLin…

找不到mfc110u.dll怎么办,总结5种有效的解决方法

在日常操作计算机的过程中,我们时常会遭遇各类突发状况,其中一种颇为常见的问题便是当试图运行某个特定软件时,系统突然弹出一则令人困扰的错误提示:“由于找不到mfc110u.dll,无法继续执行代码”。这个问题通常是由于缺…

Vol.44 一个分享网站的网站,每个月8.7万访问量

哈咯,各位朋友好啊,我是欧维,今天要给大家分享的网址是Fuun.fun,奇趣网站收藏家; 它的网址是:FUUN.FUN 这是一个我经常逛的网站,为什么我经常逛呢?因为可以从中发现一些有意思的网站…

Vol.46 一个在线小游戏网站,每个月50万访问量

大家好,我是欧维Ove,今天要给大家分享的网站是:小霸王,这是一个可以在线玩小霸王游戏的网站,网址是:小霸王,其樂無窮。紅白機,FC線上遊戲,街機遊戲,街機線上&…

一种驱动器的功能安全架构介绍

下图提供了驱动器实现安全功能的架构 具有如下特点: 1.通用基于总线或者非总线的架构。可以实现ethercat的FSOE,profinet的profisafe,或者伺服本体安全DIO现实安全功能。 2.基于1oo2D架构,安全等级可以达到sil3。 3.高可用性。单…

第17天:信息打点-语言框架开发组件FastJsonShiroLog4jSpringBoot等

第十七天 本课意义 1.CMS识别到后期漏洞利用和代码审计 2.开发框架识别到后期漏洞利用和代码审计 3.开发组件识别到后期漏洞利用和代码审计 一、CMS指纹识别-不出网程序识别 1.概念 CMS指纹识别一般能识别到的都是以PHP语言开发的网页为主,其他语言开发的网页识…

攻防世界---Web_php_include

1.题目链接 2.补充知识&#xff1a; 3.构造&#xff1a;执行成功 /?pagedata://text/plain,<?php phpinfo()?> 4.构造下面url&#xff0c;得到目录路径 /?pagedata://text/plain,<?php echo $_SERVER[DOCUMENT_ROOT]?> 5构造下面url&#xff0c;读取该路径的…

Alibaba --- 如何写好 Prompt ?

如何写好 Prompt 提示工程&#xff08;Prompt Engineering&#xff09;是一项通过优化提示词&#xff08;Prompt&#xff09;和生成策略&#xff0c;从而获得更好的模型返回结果的工程技术。总体而言&#xff0c;其实现逻辑如下&#xff1a; &#xff08;注&#xff1a;示例图…

【C++杂货铺】模板进阶

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 泛型编程 &#x1f4c1; 函数模板 &#x1f4c2; 概念 &#x1f4c2; 格式 &#x1f4c2; class 和 typename &#x1f4c2; 原理 &#x1f4c2; 函数模板实例化 &#x1f4c2; 匹配原则 &#x1f4c1; 类模板 &#x1…

全球历年GDP增长率_探数API数据统计

以下是数据的详细说明&#xff1a; 全球GDP增长最快的年份是1964年&#xff0c;全球GDP增速达到6.65%。2021年的GDP增长率也相当高&#xff0c;主要受2020年衰退后的恢复性增长推动。 全球GDP增长最慢的年份包括&#xff1a;1974年、1975年&#xff08;第一次石油危机引发&…

clion最新安装教程

还在用Dev-C吗&#xff1f;也尝试了很多C编辑器&#xff0c;不是太老&#xff0c;就是太复杂。对于c开发者来说clion真的好用&#xff0c;CLion是一款专为开发C及C所设计的跨平台IDE。难受的是cion并不免费&#xff0c;仿佛是在证明好货不贵的道理&#xff0c;只能免费用30天。…