产生死锁的四个必要条件

2024-04-15 02: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,请求资源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.chinasem.cn/article/904680

相关文章

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据

【Linux修行路】线程安全和死锁

目录 ⛳️推荐 一、线程安全 1.1 常见的线程不安全情况 1.2 常见的线程安全情况 1.3 常见的不可重入情况 1.4 常见可重入的情况 1.5 可重入与线程安全的联系 1.6 可重入与线程安全的区别 二、死锁 2.1 死锁的四个必要条件 2.2 如何避免产生死锁? ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大

如何限制与管控员工上网行为?四个方法让员工效率倍增!【企业员工上网行为管理】

在信息化时代,员工的上网行为直接影响着工作效率和企业的安全性。不当的网络使用,如浏览与工作无关的网站、下载不安全的文件,可能导致工作效率低下,甚至引发安全风险。因此,许多企业正在积极寻找有效的措施来管控员工的上网行为,以确保工作效率的提升。 以下是四个常见且有效的员工上网行为管理方法,帮助企业实现更高效的网络管理。 方法一:配置网络防火墙进行访问限制 最基础的员工上网行为管理方法是通过配置防

模拟线程死锁——Thread学习笔记

记录一下之前写过的一段模拟死锁的代码: /*** 模拟死锁** @author lixiang* @date 2018年10月12日 - 9:51* @history 2018年10月12日 - 9:51 lixiang create.*/public class HoldLockDemo {private static Object[] lock = new Object[10];priv

当网工,华为认证哪种适合我?四个维度来解惑

随着网络技术的不断进步,对网工的专业技能要求也越来越高。 在这种背景下,获得权威认证成为了提升个人技能、证明专业能力的重要途径。 华为,作为全球领先的ICT解决方案提供商,其认证项目在业界享有极高的声誉。 华为认证不仅涵盖了网络技术的各个方面,还根据不同的技能水平和职业发展阶段,提供了不同级别的认证,包括HCIA、HCIP、HCIE。 这些认证不仅有助于网络工程师提升自己的技术水平,也是企业在招聘

【编程底层思考】如何检测和避免线程死锁

一、什么是线程死锁? 线程死锁发生在多个线程因为争夺资源而相互阻塞,导致程序无法正常结束的情况。例如,线程A持有资源2并等待资源1,线程B持有资源1并等待资源2,这样就形成了死锁。 二、如何检测死锁? 使用jmap、jstack等命令行工具查看JVM的线程栈和堆内存情况,jstack可以显示死锁信息。使用VisualVM、JConsole等图形化工具进行排查。例如,JConsole可以连接到

C++11 Thread线程池、死锁、并发

一、线程与进程         进程:运行中的程序         线程:进程中的小进程 二、线程库的使用         包含头文件#include<thread> 2.1 thread函数         具体代码: void show(string str) {cout << "This is my word : " << str << endl;}int main() {t

Mysql的四个隔离级别 与mvcc(2)

特殊颜色字体为自己总结,关于mvcc直接看最后面的。 一、Mysql的四个隔离级别 预备工作: 先创建一个test数据库及account表, create database test;use test;create table account(id int not null,balance float not null,PRIMARY KEY ( id)) 向accoun

C++ 的死锁问题的发生和避免

C/C++程序中产生死锁的原因很多,本文大致归纳了下面几类,分别做分析。 1.单线程/进程多次加锁导致死锁 单线程导致死锁的情况一般是由于调用了引起阻塞的函数,比如(copy_from_user()、copy_to_ser()、和kmalloc()),阻塞后进行系统调度,调度的过程中有可能又调用了之前获取锁的函数,这样必然导致死锁。 还有一种就是自旋锁函数在没有释放锁马上又进行申请同一个自旋

OPenCV结构分析与形状描述符(4)计算一个旋转矩形的四个顶点的函数boxPoints()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 找到一个旋转矩形的四个顶点。对于绘制旋转矩形很有用。 该函数找到一个旋转矩形的四个顶点。这个函数对于绘制矩形很有帮助。在C++中,可以不用这个函数,而是直接使用RotatedRect::points方法。更多相关信息,请访问创建边界旋转框和轮廓