【多线程】什么是线程死锁?形成条件是什么?如何避免?

news/2024/11/25 21:57:06/

文章目录

  • 一、什么是线程死锁
  • 二、线程死锁
  • 三、形成死锁的四个必要条件是什么
  • 四、如何避免线程死锁

一、什么是线程死锁

死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

在这里插入图片描述

二、线程死锁

下面通过一个例子来说明线程死锁,代码模拟了上图的死锁的情况 (代码来源于《并发编程之美》输出结果

public class DeadLockDemo {private static Object resource1 = new Object();//资源 1private static Object resource2 = new Object();//资源 2public static void main(String[] args) {new Thread(() -> {synchronized (resource1) {System.out.println(Thread.currentThread() + "get resource1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resource2");synchronized (resource2) {System.out.println(Thread.currentThread() + "get resource2");}}}, "线程 1").start();new Thread(() -> {synchronized (resource2) {System.out.println(Thread.currentThread() + "get resource2");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resource1");synchronized (resource1) {System.out.println(Thread.currentThread() + "get resource1");}}}, "线程 2").start();}
}

输出结果

Thread[线程 1,5,main]get resource1
Thread[线程 2,5,main]get resource2
Thread[线程 1,5,main]waiting get resource2
Thread[线程 2,5,main]waiting get resource1

线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过Thread.sleep(1000);让线程 A 休眠 1s 为的是让线程 B 得到CPU执行权,然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。上面的例子符合产生死锁的四个必要条件。

三、形成死锁的四个必要条件是什么

  1. 互斥条件:线程(进程)对于所分配到的资源具有排它性,即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放
  2. 请求与保持条件:一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
  4. 循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞

四、如何避免线程死锁

我们只要破坏产生死锁的四个条件中的其中一个就可以了。

  • 破坏互斥条件
    这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。
  • 破坏请求与保持条件
    一次性申请所有的资源。
  • 破坏不剥夺条件
    占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
  • 破坏循环等待条件
    靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
    我们对线程 2 的代码修改成下面这样就不会产生死锁了。
new Thread(() -> {synchronized (resource1) {System.out.println(Thread.currentThread() + "get resource1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resource2");synchronized (resource2) {System.out.println(Thread.currentThread() + "get resource2");}}
}, "线程 2").start();

输出结果

Thread[线程 1,5,main]get resource1

我们分析一下上面的代码为什么避免了死锁的发生?

线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。
然后线程 1 再去获取 resource2 的监视器锁,可以获取到。
然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。
这样就破坏了破坏循环等待条件,因此避免了死锁。


http://www.ppmy.cn/news/75070.html

相关文章

STL --- 2、容器 set 和multiset

std::set 和 std::multiset 都是 STL 中的关联容器,用于存储一组有序的元素。 1、std::set 和 std::multiset 特点 (1)std::set 中每个元素的键值都唯一,而 std::multiset 中可以有多个相同的键值。 (2)s…

Linux搭建MQTT服务器(mosquitto)并使用

零、码仙励志 在路上,寻找一个继续的理由,寻找一个曾经的梦想。 一、Linux搭建MQTT服务器(mosquitto)并使用 1、安装依赖 yum install gcc-c cmake openssl-devel libuuid-devel c-ares-devel uuid-devel libwebsockets-devel…

HP打印機维护怎么找官方资料

关键词: “Removal and replacement” "part number" 范例 Removal and replacement A7W93 67033 Removal and replacement A7W93-67081 HP PageWide Enterprise, HP PageWide Managed - Removal and replacement: Service fluid container kit | H…

Linux之NetLink学习笔记

1.NetLink机制 NetLink是一种基于应用层跟内核态的通信机制,其特点是一种异步全双工的通信方式,支持内核态主动发起通信的机制。该机制提供了一组特殊的API接口,用户态则通过socket API调用。内核发送的数据再应用层接收后会保存在接收进程socket的缓存…

数据结构与算法之散列表详解

一、散列表概述 散列表(Hash Table)也叫哈希表,它是一种时间复杂度能够达到接近常数的数据结构,可以用来快速地存储和查找数据。散列表通过哈希函数来将键值对映射为一个索引值,然后通过这个索引值来在数组中访问对应…

【数据库】SQLServer报修表,维修表,反馈表三表连查

大家好,我是雷工! 最近参与的一个SCADA项目,客户要求增加设备维保的功能,对设备的报修,维修,反馈过程进行记录查询,进一步提升企业的信息化能力。 该过程的实现是通过创建三个表分别记录报修-维…

【0196】共享内存管理结构(shmem)之创建共享内存分配机制(Shared Memory Allocation)(2)

文章目录 1. 共享内存段(Shared Memory Segment)1.1 设置指向共享内存的基本指针2. 创建共享内存分配机制相关文章: 【0195】共享内存管理结构(shmem)之概念篇(1) 【0

Linux下安装配置maven

1.安装以及配置maven 1.1.下载maven安装包 首先需要切换到自己需要安装的目录 把配置都放到了:/root路径下 1.2.解压下载好的maven包 tar -zxvf apache-maven-3.6.0-bin.tar.gzcp -r apache-maven-3.6.0 /usr/local/1.3.配置maven环境变量 1.3.1.在环境变量中…