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

什么是线程死锁

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

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

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

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

  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,一经查实,立即删除!

相关文章

TortoiseGit的使用详解

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

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

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

BufferedImage类、Image类、Graphics类

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

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…

VUE学习笔记详细

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

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…

Jenkins学习笔记详细

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

Form Data与Request Payload,你真的了解吗?

前言 做过前后端联调的小伙伴&#xff0c;可能有时会遇到一些问题。例如&#xff0c;我明明传递数据给后端了&#xff0c;后端为什么说没收到呢&#xff1f;这时候可能就会就会有小伙伴陷入迷茫&#xff0c;本文从chrome-dev-tools&#xff08;F12调试器&#xff09;中看到的F…

计算机网络知识点复习

基础 1.说下计算机网络体系结构 计算机网络体系结构&#xff0c;一般有三种&#xff1a;OSI 七层模型、TCP/IP 四层模型、五层结构。 简单说&#xff0c;OSI是一个理论上的网络通信模型&#xff0c;TCP/IP是实际上的网络通信模型&#xff0c;五层结构就是为了介绍网络原理而折…

n个1组成的整数能被2013整除c语言,求大神解算法,“编写程序,求n至少为多大时,n个1组成的整数能被2013 整除。”...

编写程序&#xff0c;求n至少为多大时&#xff0c;n个1组成的整数能被2013 整除。使用python黑科技:i 1while int(1 * i) % 2013:i 1print(i)不使用黑科技:i s t 1while s % 2013:i 1t t * 10 % 2013s (s t) % 2013print(i)而事实上可以从数论的角度看。20133*11*61&a…

Java基础知识点复习

转载&#xff1a;https://mp.weixin.qq.com/s/M-6RSRcRd3X93cR7VXpanw Java概述 1.什么是Java&#xff1f; Java是一门面向对象的编程语言&#xff0c;不仅吸收了C语言的各种优点&#xff0c;还摒弃了C里难以理解的多继承、指针等概念&#xff0c;因此Java语言具有功能强大和…

为什么都说Dubbo不适合传输大文件?Dubbo支持的协议

背景 之前公司有一个 Dubbo 服务&#xff0c;内部封装了腾讯云的对象存储服务 SDK&#xff0c;是为了统一管理这种三方服务的SDK&#xff0c;其他系统直接调用这个对象存储的 Dubbo 服务。用来避免因平台 SDK 出现不兼容的大版本更新&#xff0c;导致公司所有系统修改跟着升级…

c语言编写劫持dll,c语言-----劫持自己02

在上一节 c语言-----劫持原理01 已经叙述了劫持原理&#xff0c;下边正式进入劫持实战1. 需要实现的功能在c语言中system("notepad") 可以打开一个记事本system("mspaint") 可以打开画图工具所以这次我们需要把 可以打开一个记事本 这个功能更改为 在控制…

Java中Runtime类

一、概述 Runtime类封装了运行时的环境。每个 Java 应用程序都有一个 Runtime 类实例&#xff0c;使应用程序能够与其运行的环境相连接。 一般不能实例化一个Runtime对象&#xff0c;应用程序也不能创建自己的 Runtime 类实例&#xff0c;但可以通过 getRuntime 方法获取当前R…

在Java中调用Python

写在前面 在微服务架构大行其道的今天&#xff0c;对于将程序进行嵌套调用的做法其实并不可取&#xff0c;甚至显得有些愚蠢。当然&#xff0c;之所以要面对这个问题&#xff0c;或许是因为一些历史原因&#xff0c;或者仅仅是为了简单。恰好我在项目中就遇到了这个问题&#…

android 导航动画,安利一个Android导航库

SlidingRootNav这是一个像DrawerLayout一样的抽屉式的导航库&#xff0c;这个库实现的抽屉在content view的下层&#xff0c;滑动之后&#xff0c;才能看到相应的导航页使用Gradle添加依赖compile com.yarolegovich:sliding-root-nav:1.0.2使用说明创建一个 content_view.xml或…

android 界面组件,安卓开发学习周第三篇——Android中的UI组件

原标题&#xff1a;安卓开发学习周第三篇——Android中的UI组件在Android APP中&#xff0c;所有的用户界面元素都是由View和ViewGroup的对象构成的。View是绘制在屏幕上的用户能与之交互的一个对象。而ViewGroup则是一个用于存放其他View(和ViewGroup)对象的布局容器&#xff…