20.线程的活性故障:死锁

2023-12-20 20:58
文章标签 线程 死锁 故障 20 活性

本文主要是介绍20.线程的活性故障:死锁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 1.定义
  • 2.死锁产生的条件
  • 3.规避死锁的方法
  • 4.死锁的恢复

1.定义

死锁是线程的一种常见活性故障。如果两个或者更多的线程因相互等待对方而被永远暂停(线程的生命周期状态为 BLOCKED 或者 WAITING), 那么我们就称这些线程产生了死锁(Deadlock)。 由于产生死锁的线程的生命周期状态永远是非运行状态,因此这些线程所要执行的任务也永远无法进展。死锁产生的一种典型情形如下图。
在这里插入图片描述

2.死锁产生的条件

线程一旦产生死锁,那么这些线程及相关的资源将满足如下全部条件。

  • 资源互斥。涉及的资源必须是独占的,即每个资源一次只能够被一个线程使用。
  • 资源不可抢夺。涉及的资源只能够被其持有者(线程)主动释放,而无法被资源的持有者和申请者之外的第三方线程所抢夺(被动释放)。
  • 占用并等待资源。涉及的线程当前至少持有一个资源(资源 A)并申请其他资源(资源B),而这些资源(资源 B) 恰好被其他线程持有 。 在这个资源等待的过程中,线程并不释放其已经持有的资源 。
  • 循环等待资源。涉及的线程必须在等待别的线程持有的资源,而这些线程又反过来在等待第1个线程所持有的资源。

这些条件是死锁产生的必要条件而非充分条件,也就是说只要产生了死锁,那么上面这些条件一定同时成立,但是上述条件即使同时成立也不一定就能产生死锁 。

3.规避死锁的方法

由于锁具有排他性并且锁只能够由其持有线程主动释放,因此由锁导致的死锁只能够从消除“占用并等待资源”和消除“循环等待资源”这两个方向入手 。

  • 粗锁法——使用粗粒度的锁代替多个锁。从消除“占用并等待资源”出发我们不难想到的一种方法就是,采用一个粒度较粗的锁来替代原先的多个粒度较细的锁,这样涉及的线程都只需要申请一个锁从而避免了死锁。 粗锁法的缺点是它明显地降低了并发性并可能导致资源浪费。
  • 锁排序法——相关线程使用全局统一的顺序申请锁。假设有多个线程需要申请资源(锁) {Lock1, Lock2. …, LockN},那么我们只需要让这些线程依照一个全局(相对于使用这种资源的所有线程而言)统一的顺序去申请这些资源,就可以消除“循环等待资源”这个条件,从而规避死锁。
  • 规避死锁的第3种方法是使用 ReentrantLock.tryLock(long,TimeUnit) 申请锁 。ReentrantLock.tryLock(long,TimeUnit)允许我们为锁申请这个操作指定一个超时时间。在超时时间内,如果相应的锁申请成功,那么该方法返回 true; 如果在tryLock(long,TimeUnit)执行的那一刻相应的锁正被其他线程持有,那么该方法会使当前线程暂停,直到这个锁被申请成功(此时该方法返回 true) 或者等待时间超过指定的超时时间(此时该方法返回false)。因此,使用tryLock(long,TimeUnit)来申请锁可以避免一个线程无限制地等待另外一个线程持有的资源,从而最终能够消除死锁产生的必要条件中的“占用并等待资源” 。
  • 一般地,一个方法在持有一个锁的情况下调用一个外部方法,而外部方法往往不在我们(开发人员)的控制范闱之内,其自身可能不会申请另外一个锁,也可能会申请另外一个锁。因此,在持有一个锁的情况下调用一个外部方法的代码很可能会间接导致死锁 。这种情况可以使用开放调用来规避。所谓开放调用 (Open Call) 就是一个方法在调用外部方法的时候不持有任何锁。显然,开放调用能够消除死锁产生的必要条件中的“持有并等待资源”。
  • 使用锁的替代品

4.死锁的恢复

死锁自动恢复的实际意义并不大。原因:

  • 死锁的自动恢复有赖于死锁的线程能够响应中断。
  • 自动恢复尝试可能导致新的问题。

这篇关于20.线程的活性故障:死锁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/517479

相关文章

Java中的xxl-job调度器线程池工作机制

《Java中的xxl-job调度器线程池工作机制》xxl-job通过快慢线程池分离短时与长时任务,动态降级超时任务至慢池,结合异步触发和资源隔离机制,提升高频调度的性能与稳定性,支撑高并发场景下的可靠... 目录⚙️ 一、调度器线程池的核心设计 二、线程池的工作流程 三、线程池配置参数与优化 四、总结:线程

WinForm跨线程访问UI及UI卡死的解决方案

《WinForm跨线程访问UI及UI卡死的解决方案》在WinForm开发过程中,跨线程访问UI控件和界面卡死是常见的技术难题,由于Windows窗体应用程序的UI控件默认只能在主线程(UI线程)上操作... 目录前言正文案例1:直接线程操作(无UI访问)案例2:BeginInvoke访问UI(错误用法)案例

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性: