线程死锁——死锁产生的条件

什么是线程死锁

线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于相互等待状态,若无外力作用,它们将无法继续执行下去。

造成死锁的原因可以概括成三句话:

  • 当前线程拥有其他线程需要的资源
  • 当前线程等待其他线程已拥有的资源
  • 不放弃自己拥有的资源

线程死锁产生的四个必要条件

  1. 互斥,共享资源 X 和 Y 只能被一个线程占用;
  2. 占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
  3. 不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
  4. 循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。

举个必然产生死锁的例子

public static void main(String[] args) {Object a = new Object();Object b = new Object();// 线程1new Thread(() -> {synchronized (a) {System.out.println("获得了A锁");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (b) {}}}).start();// 线程2new Thread(() -> {synchronized (b) {System.out.println("获得了B锁");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (a) {}}}).start();
}

上面的程序就是一个典型死锁的例子,为了保证死锁发生的几率,我这里在获得锁之后睡眠了1s。

线程1在获得A对象锁之后等了1s去尝试获取B对象锁,这时线程1是持有A对象锁的;线程2在获得B对象锁之后等待1s去尝试获得A对象锁,这时线程2是持有B对象锁的;就在它们彼此想获得对方的锁的时候,死锁发生了,并且一直持续下去。
img

如何避免死锁

上面提到只有这四个条件都发生时才会出现死锁,那么意思就是说,只要我们破坏其中一个,就可以成功预防死锁的发生。

  • 破坏互斥:只有一把锁,这是形成死锁的最关键的原因。显然,如果我们能在两个线程跑之前,能给每个线程单独拷贝一份钥匙的副本,就能有效的避免死锁了。
  • 占用且等待一次性申请所有的资源,这样就不存在等待了。
    例如线程1一次性拿到A和B两个锁,线程2在获取锁的时候需要等待线程1释放锁,这样就避免了多线程互相占用等待的情况。
  • 不可抢占:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
    在上面的死锁代码中,我们使用了synchronized关键字,它是不能主动释放资源的,会造成线程一直阻塞,JUC提供了Lock解决这个问题。
    显式使用Lock类中的定时tryLock功能来代替内置锁机制,可以检测死锁和从死锁中恢复过来。显式锁可以指定一个超时时限(Timeout),在等待超过该时间后tryLock就会返回一个失败信息,释放其拥有的资源,其他线程可以获取此资源避免死锁。
  • 循环等待:如果一个线程需要一些锁,那么它必须按照确定的顺序获取锁。只有先获得了从顺序上排在前面的锁之后,才能获取后面的锁。
    破坏循环条件很简单,只要线程之间不要出现交叉占用的情况即可,也就是说在在代码中尽量避免线程1保持A请求B,线程2保持B请求A,尽可能使他们请求的顺序一致,比如线程1请求的顺序是A、B,线程2请求的顺序也是A、B,这样自然就避免了循环等待的情况发生。

总结

死锁是一个比较头疼的问题,但是只要我们的代码规范,可以避免大多数情况下的死锁。还有避免死锁的经典算法是银行家算法,这里就不扩开介绍了。

在很多情况下,尤其是多线程编程中,我们要注意线程之间的资源是否存在互相竞争的情况,如果有,要及时规避死锁的风险。

死锁很多时候会发生在数据库操作中,例如长事务、并发条件下的共享锁升级等都会造成数据库死锁,后面有时间会专门针对数据库死锁讲一讲。

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

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

相关文章

linux查看正在运行的窗口,获取linux中打开的应用程序窗口的数量

我想检测由窗口管理器管理的特定应用程序的实例数量.目前,我有这个&#xff1a;#!/bin/bash# wmctrl requiredpids$(pidof $1)IFS read -a pid_arr <<< "$pids"matches0for pid in "${pid_arr[]}"domatching_lines$(wmctrl -l -p | egrep -c &qu…

TortoiseGit的使用详解

Git是什么&#xff0c;相信大家都很清楚。Git不就是分布式版本控制系统嘛&#xff1f;那你知道TortoiseGit是什么吗&#xff1f;下面我们就介绍一下TortoiseGit它是什么&#xff1f;如何使用&#xff1f;   TortoiseGit其实是一款开源的git的版本控制系统&#xff0c;也叫海龟…

linux gpt分区看不到,Linux无法看到我的任何分区 – 备份GPT表不在磁盘的末尾

我正在尝试在HP Pavilion 14英寸超极本上安装Linux,但没有任何成功.起初我尝试在其上安装Ubuntu;一切都很顺利,我进入了Live DVD(是的,我就像那样老了),然后去我的磁盘上安装系统.发生的第一个奇怪的事情是,我没有被提示选择在Windows旁边安装Ubuntu,而是直接用分区表抛入窗口.…

将项目上传到Gitee上(命令方式使用TortoiseGit方式)

如何将项目上传到Gitee上&#xff08;命令方式&#xff09; 目录 将项目上传到Gitee是我们经常需要使用到的操作&#xff0c;因此我们要熟悉这些步骤 一、首先保证本机已经安装了Git git官网安装完成之后&#xff0c;鼠标右键会出现Git GUI Here和Git Bash Here 二、上传代…

linux自动重新启动,linux 系统自动重新启动,请帮忙看看

在查了一下,的确有这个log其中有一段之后系统开始重新启动&#xff0c;请帮忙看看是什么原因&#xff1a;谢谢[2011-01-25 11:33:36 xend.XendDomainInfo 2990] DEBUG (XendDomainInfo:228) XendDomainInfo.recreate({paused: 0, cpu_time: 41195236230L, ssidref: 0, hvm: 0, …

java.awt.Color类

Color类概述 Color是用来封装颜色的&#xff0c;支持多种颜色空间&#xff0c;默认为RGB颜色空间。每个Color对象都有一个alpha通道&#xff0c;值为0到255&#xff0c;代表透明度&#xff0c;当alpha通道值为255时&#xff0c;表示完全不透明&#xff1b;当alpha通道值为0时&…

BufferedImage类、Image类、Graphics类

BufferedImage Image是一个抽象类&#xff0c;BufferedImage是其实现类&#xff0c;是一个带缓冲区图像类&#xff0c;主要作用是将一幅图片加载到内存中&#xff08;BufferedImage生成的图片在内存里有一个图像缓冲区&#xff0c;利用这个缓冲区我们可以很方便地操作这个图片&…

linux远程连接最大数是多少,Linux Shell 脚本限制ssh最大用户登录数

我撰写本文原来的意图是想把“复制SSH渠道”和"copy SSH Session"这样的功能从远程ssh客户端中剔除掉.因此想到可以在SSH服务端设置一下&#xff0c;但查阅了sshd_config的man手册,发现里面的看起来限制ssh连接数量的参数(MaxSessions &#xff0c;ClientAliveCountM…

linux 文件名带特殊符号,Linux删除含有特殊符号文件名的文件

Web前端面试题目及答案汇总HTML/CSS部分 1.什么是盒子模型? 在网页中,一个元素占有空间的大小由几个部分构成,其中包括元素的内容(content),元素的内边距(padding),元素的边框(border),元素的外边 ...Delphi中滚动文字的应用1.添加一个Timer控件,Interval属性设置为20. 2.添加…

Vue this.$refs的作用

案例一、ref 写在标签上时 <!-- ref 写在标签上时&#xff1a;this.$refs.名字 获取的是标签对应的dom元素ref 写在组件上时&#xff1a;这时候获取到的是 子组件&#xff08;比如counter&#xff09;的引用--><div id"root"><!-- ref hello&#…

linux电脑合盖后卡住了,解决ubuntu合盖后无法唤醒

解决办法&#xff1a;安装laptop-mode-tools工具包。1.检查是否安装了grep laptop-mode-tools 工具包$ dpkg -l | grep laptop-mode-tools如果执行命令无结果输出&#xff0c;表示未安装(如果已安装&#xff0c;忽略第2步)2.安装laptop-mode执行命令&#xff1a;$ sudo apt-get…

三列布局 css

实现如下图的三列布局&#xff1a; .box {width:1400px;margin:0 auto;padding-bottom:40px;> .left {float:left;width:180px;margin-top:100px;text-align:center;}> .center {float:left;margin-top:100px;margin-left:130px;item-box {float:left;text-align:left;…

axios和ajax的区别是什么

axios和ajax的区别&#xff1a; 1、axios是一个基于Promise的HTTP库&#xff0c;而ajax是对原生XHR的封装&#xff1b; 2、ajax技术实现了局部数据的刷新&#xff0c;而axios实现了对ajax的封装。 axios和ajax的区别是什么? axios和ajax的区别及优缺点: ajax&#xff1a; 1…

telnet linux 命令详解,解析Linux Telnet命令

不少系统中&#xff0c;都有Telnet的相关涉及和使用。那么今天我们就来讲解一下Linux Telnet命令的相关使用和操作。这里我们针对一些重点的内容和命令进行讲解。希望对大家有所帮助。用户使用Telnet命令进行远程登录。该命令允许用户使用Telnet协议在远程计算机之间进行通信&a…

VUE学习笔记详细

VUE学习笔记 本文章以vue3来记录的&#xff0c;但并非记录vue3所有变化&#xff01; 1、ES6语法 1.1、let变量声明 let用于声明变量有局部作用域let声明的变量不会提升&#xff08;只能先定义后使用&#xff09; 1.2、const变量声明 const用于声明常量const声明的常量也不会…

C语言2020年作业,2020年c语言上机报告范文【四篇】

《2020年c语言上机报告范文【四篇】》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《2020年c语言上机报告范文【四篇】(7页珍藏版)》请在人人文库网上搜索。1、2020 年 c 语言上机报告范文【四篇】2020 年 c 语言上机报告范文一说到我学习 C 语言时&#xff0c;真是用…

Centos7配置gitlab服务器

Centos7配置gitlab服务器 1、安装SSH yum install -y curl policycoreutils-pythonopenssh-server设置开机自启 sudo systemctl enable sshd启动服务 sudo systemctl start sshd2、安装postfix 邮件服务 sudo yum install postfix设置开机自启 sudo systemctl enable po…

湖南工大11级C语言网上作业,湖南工大11级C语言网上作业之《最简单的程序设计》.docx...

《程序设计语言 C1》随机作业题做作业时间&#xff1a;2012-3-9 8:00:00至2012-5-29 23:30:00?1、以下程序的输出结果是 main() { char cz; printf("%c",c-25); } (3分)A、B、C、D、A、aB、ZC、z-25D、y?2、以下程序段的输出结果是 char s[]"\\141\141abc\t&q…

Jenkins学习笔记详细

最近接触了jenkins这个东西&#xff0c;所以花点时间了解了下。它可以在代码上传仓库&#xff08;如github,gitee&#xff0c;gitlab&#xff09;后&#xff0c;在jenkins&#xff08;一个网站界面&#xff09;中通过获取代码仓库中最新代码&#xff0c;进行自动化部署&#xf…

c语言程序设计华北电力大学,2016年华北电力大学电气与电子工程学院C语言程序设计(同等学力加试)考研复试题库...

一、选择题1&#xff0e; 有以下程序程序运行后的输出结果是( )。A.24B.44C.22D.46答:B【解析】p 是int 类型的指针&#xff0c;指向数组a 的首元素&#xff1b;k 的int*类型的指针&#xff0c;指向int*变量先执行p&#xff0c;p 指向a 中第二个元素&#xff0c;然后取值&#…