产生死锁的四个必要条件

产生死锁的四个必要条件

  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,请求资源2Thread 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,请求资源1Thread 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.rhkb.cn/news/308946.html

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

相关文章

Google Imagen 2对比OpenAI的Dall-E 3 - 同一提示,不同结果

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

咸鱼之王_手游_开服搭建架设_内购修复无bug运营版

视频演示 咸鱼之王_手游_开服 游戏管理后台界面 源码获取在文章末尾 源码获取在文章末尾 源码获取在文章末尾 或者直接下面 https://githubs.xyz/y28.html 1.安装宝塔 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh &…

【C++】深度解析---赋值运算符重载(小白一看就懂!!)

目录 一、前言 二、 运算符重载 🍎运算符重载 ① 概念引入 ② 语法明细 ③ 练习巩固 ④ 代码展示 🍇赋值运算符重载 ① 语法说明及注意事项 ② 默认的赋值运算符重载 ③ 值运算符不能重载成全局函数! 三、总结 四、共勉 一、前言…

centos 7.9 nginx本地化安装,把镜像改成阿里云

1.把centos7.9系统切换到阿里云的镜像源 1.1.先备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup1.2.下载新的CentOS-Base.repo配置文件 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo特别…

本地web项目启起来后,无法在浏览器(chrome)看到源码,从而无法打断点;Framework Ignore list

问题描述 本地web项目启起来后,无法在浏览器(chrome)看到源码,从而无法打断点 其他浏览器没看,开发环境一致专注于chrome(其余浏览器有测试同事提缺陷了,才会去看),其余浏览器有没有这个问题&…

linux 内存寻址

(持续更新) 相关概念 查看的书籍为 深入linux内核 内存地址 当使用80x86(32位)微处理器时,一般分为三种不同的地址: 逻辑地址 包含在机器语言指令中用来指定一个操作数或一条指令的地址。每一个逻辑地址…

C#简单工厂模式的实现

using System.Diagnostics.Metrics; using System.Runtime.InteropServices; using static 手写工厂模式.Program;namespace 手写工厂模式 {internal class Program{public interface eats {void eat();}//定义了一个接口public class rice : eats{public void eat() {Console.…

neo4j-01

Neo4j是: 开源的(社区版开源免费)无模式(不用预设数据的格式,数据更加灵活)noSQL(非关系型数据库,数据更易拓展)图数据库(使用图这种数据结构作为数据存储方…

Flutter第七弹 网格列表GridView

1) Flutter提供了网格列表,怎么设置列数? 2)怎么初始化每个列表项Item? 一、GridView简介 Flutter也存在网格列表组建GridView,用于展示多行多列的列表。 1.1 GridView构建 采用GridView.count() 进行构建 1.2 Gr…

HarmonyOS实战开发-自定义分享

介绍 自定义分享主要是发送方将文本,链接,图片三种类型分享给三方应用,同时能够在三方应用中展示。本示例使用数据请求 实现网络资源的获取,使用屏幕截屏 实现屏幕的截取,使用文件管理 实现对文件,文件目录的管理&…

5.9 mybatis之callSettersOnNulls作用

文章目录 1. 当callSettersOnNullstrue时2. 当callSettersOnNullsfalse时 在mybatis的settings配置参数中有个callSettersOnNulls参数,官方解释为:指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法…

Collection与数据结构 二叉树(三):二叉树精选OJ例题(下)

1.二叉树的分层遍历 OJ链接 上面这道题是分层式的层序遍历,每一层有哪些结点都很明确,我们先想一想普通的层序遍历怎么做 /*** 层序遍历* param root*/public void levelOrder1(Node root){Queue<Node> queue new LinkedList<>();queue.offer(root);while (!qu…

Geeker-Admin:基于Vue3.4、TypeScript、Vite5、Pinia和Element-Plus的开源后台管理框架

Geeker-Admin&#xff1a;基于Vue3.4、TypeScript、Vite5、Pinia和Element-Plus的开源后台管理框架 一、引言 随着技术的不断发展&#xff0c;前端开发领域也在不断演变。为了满足现代应用程序的需求&#xff0c;开发人员需要使用最新、最强大的工具和技术。Geeker-Admin正是…

Ubuntu 22上安装Anaconda3。下载、安装、验证详细教程

在Ubuntu 22上安装Anaconda3&#xff0c;你可以遵循以下步骤&#xff1a; 更新系统存储库&#xff1a; 打开终端并运行以下命令来更新系统存储库&#xff1a; sudo apt update安装curl包&#xff1a; 下载Anaconda安装脚本通常需要使用curl工具。如果系统中没有安装curl&#x…

流媒体的安全谁来保障

流媒体的安全谁来保障 说起媒体&#xff0c;我们马上就会想到报纸新闻、广播、电视。 其实所谓的流媒体同我们通常所指的媒体是不一样的&#xff0c; 它只是一个技术名词。流媒体到底是什么&#xff1f;能给我们的生活带来什么&#xff1f;跟小德一起来看看。 流媒体是什么&a…

OSI七层网络模型 —— 筑梦之路

在信息技术领域&#xff0c;OSI七层模型是一个经典的网络通信框架&#xff0c;它将网络通信分为七个层次&#xff0c;每一层都有其独特的功能和作用。为了帮助记忆这七个层次&#xff0c;有一个巧妙的方法&#xff1a;将每个层次的英文单词首字母组合起来&#xff0c;形成了一句…

REINFORCE及进阶算法讲解笔记

REINFORCE 总结 估计VALUE-methods没有在理论上证明收敛&#xff0c;而policy-methods不需要估计value function。 本算法总结了过去的算法&#xff0c;将过去算法作为特例看待&#xff0c;证明了即使是结合函数估计和实际采样的value梯度都可以无偏估计&#xff0c;证明了某种…

PC-lint 学习之配置方法

1. 下载PC-lint 9.0后&#xff0c;点击pclint9setup.exe进行安装&#xff08;我只安装了C/C语言&#xff0c;其他语言可安装时选择&#xff09; 2.安装完成后&#xff0c;打开keil5&#xff0c;选择配置 3. 配置选项 &#xff08;1&#xff09;Lint Executable&#xff1a;在第…

知识图谱与人工智能:携手共进

知识图谱与人工智能&#xff1a;携手共进 一、引言&#xff1a;知识图谱与人工智能的融合 在这个数据驱动的时代&#xff0c;知识图谱与人工智能&#xff08;AI&#xff09;之间的融合不仅是技术发展的必然趋势&#xff0c;也是推动各行各业创新的关键。知识图谱&#xff0c;作…

docker 上达梦导入dump文件报错:本地编码:PG GBK,导入女件编码:PGGB18030

解决方案&#xff1a; 第一步进入达梦数据容器内部 docker exec -it fc316f88caff /bin/bash 第二步&#xff1a;在容器中 /opt/dmdbms/bin目录下 执行命令 cd /opt/dmdbms/bin./dimp USERIDSYSDBA/SYSDBA001 FILE/opt/dmdbms/ZFJG_LJ20240407.dmp SCHEMASZFJG_LJUSERIDSYSD…