【Java多线程进阶】死锁

news2024/11/28 20:56:41

前言

死锁(Deadlock)是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如果不提前预防或外界干扰,这些线程将无法执行下去。因此,本篇博文讲解造成死锁的原因以及解决方案。

目录

1. 造成死锁的原因

2. 哲学家就餐问题

2.1 造成死锁的原因

2.2 解决死锁问题


1. 造成死锁的原因

死锁是多个线程在相互等待对方所占有的锁才能继续执行下去,造成线程都被阻塞都无法执行下去,也释放不了线程自己的锁资源,造成僵局。

举例说明:

我和朋友去拉面馆吃面,我先拿起了辣椒他拿起了醋。此时他想要辣椒、我想要醋,但我俩谁也不让谁。

我对他说:你先把辣椒给我,我用完了就把醋给你。他对我说:你先把醋给我,我用完了就把辣椒给你。

我和他也不让谁,最后导致死锁。在上述例子中,辣椒和醋就是两个锁,我和他就是两个线程。关于死锁会引申的一个哲学家就餐问题。


2. 哲学家就餐问题

哲学家就餐问题是一个经典的并发编程问题,它描述的是五个哲学家围坐在一张圆形餐桌旁,每个哲学家面前有一碗饭和一只筷子,碗和筷子都是共享资源。

哲学家需要用一双筷子来吃饭,但是只有相邻的两个哲学家之间有一只筷子,因此他们必须在餐桌上竞争筷子,才能拿到一双筷子吃饭。

这个问题会引发死锁(Deadlock)和饥饿(Starvation)等并发编程中的经典问题。

如果所有哲学家同时都想获取餐具,但每个哲学家只能等待旁边的哲学家放下筷子才能获取餐具,这可能会导致死锁。

如果所有哲学家只有在其他人都吃到饭才能拿到自己的餐具,那么有些哲学家可能会因为等待时间太长而饥饿。

代码案例:

有两个哲学家在进行就餐,哲学家1获取到筷子1后继续尝试获取筷子2,哲学家2获取到筷子2后继续尝试获取筷子1,造成了死锁状态。

public class DeadlockDemo {
        public static void main(String[] args) {
            Object obj1 = new Object();//筷子1
            Object obj2 = new Object();//筷子2

            // 线程1获取obj1,然后等待obj2
            Thread thread1 = new Thread(() -> {
                synchronized (obj1) {
                    System.out.println(Thread.currentThread().getName() + " 获取 obj1锁");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj2) {
                        System.out.println(Thread.currentThread().getName() + " 获取 obj2锁");
                    }
                }
            }, "哲学家 1");

            // 线程2获取obj2,然后等待obj1
            Thread thread2 = new Thread(() -> {
                synchronized (obj2) {
                    System.out.println(Thread.currentThread().getName() + " 获取 obj2锁");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (obj1) {
                        System.out.println(Thread.currentThread().getName() + " 获取 obj1锁");
                    }
                }
            }, "哲学家 2");

            t1.start();
            t2.start();
        }
}

运行后打印:

上述代码,中 thread1 是哲学家1,thread2 是哲学家2,obj1 锁和 obj2 锁分别代表两只筷子。哲学家1 占用了 obj1 锁,哲学家2 占用了 obj2 锁。

此时 哲学家1 在拥有 obj1 时获取 obj2 是获取不到的,哲学家2 在拥有 obj2 时获取 obj1 也是获取不到的。

这样就会造成死锁。也就是本篇文章开头所说的,一个线程处于永久获取到锁的状态,其他线程无限阻塞等待,这就造成了死锁。


2.1 造成死锁的原因

  • 互斥:一个资源只能被一个进程独占,即该资源只能被一个进程占有或使用。
  • 请求和保持:进程已经保持了至少一个资源,并还在尝试获取其他进程保持的资源。
  • 不可抢占:某个资源只能由占有它的线程释放,不能被其他线程强制占有或抢占。
  • 循环等待:假设一个队列有{t1,t2,t3}这三个线程,t1 等待 t2 所占资源,t2 等待 t3 所占资源,t3 等待 t1 所占资源,这样就会造成循环等待。 

2.2 解决死锁问题

解决死锁问题,最好的方式就是预防死锁。我们把锁按照顺序进行编号,并且约定每个线程在获取多把锁的时候必须先获取编号小的,后获取编号大的。这样就不会形成死锁了。

因为,最先获取编号小锁的线程会优先完成相应任务。轮到其他线程再获取小编号锁时,锁就处于空闲状态了。

代码案例:

public class DeadlockDemo {
        public static void main(String[] args) {
            Object obj1 = new Object();//筷子1
            Object obj2 = new Object();//筷子2

            
            Thread t1 = new Thread(() -> {
                synchronized (obj1) {
                    System.out.println(Thread.currentThread().getName() + " 获取 obj1锁");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj2) {
                        System.out.println(Thread.currentThread().getName() + " 获取 obj2锁");
                    }
                }
            }, "哲学家 1");

            
            Thread t2 = new Thread(() -> {
                synchronized (obj1) {
                    System.out.println(Thread.currentThread().getName() + " 获取 obj2锁");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (obj2) {
                        System.out.println(Thread.currentThread().getName() + " 获取 obj1锁");
                    }
                }
            }, "哲学家 2");

            t1.start();
            t2.start();
        }
}

运行后打印:

在上述代码中,我们创建了两个锁对象 obj1 和 obj2 分别代表两只筷子,并创建两个线程 thread1 和 thread 2 分别代表两个哲学家。

当 哲学家1 首先获取到 obj1 后,由于 obj2 并未线程占用,则立马获取 obj2 ,获得了两双筷子,就餐后释放 obj1 和 obj2。

这时 哲学家2 尝试获取 obj2 和 obj1 时,发现两双筷子都没人使用,直接开始就餐。


什么是死锁?

当多个线程并发后,线程在占有锁资源的情况下还尝试获取别的线程所占有的锁资源。这样就会操作线程永远获取不到其他锁资源,也不能释放本身锁资源。从而形成的一种僵局,这就是死锁。

造成死锁的原因是什么?

造成死锁的原因有,互斥、请求和保持、不可抢占、循环等待。(文章中有讲解)

死锁的解决方案

我们可以对多个锁按照顺序进行编号,并约定每个线程在使用多把锁时优先使用最小编号的锁,这样就能保证其他线程再获取小编号锁时,锁资源已得到释放。


本篇博文到这里就结束了,感谢点赞、评论、收藏、关注~

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

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

相关文章

7.条件渲染

目录 1 wx:if 2 wx:elif 3 wx:else 4 block标签配合条件渲染 5 hidden 1 wx:if wx:if 与 v-if 的运行方式相同,都是 删除与创建元素(不是display:none) 2 wx:elif 3 wx:else 4 block标签配合条件渲染 我们现在有一个view套view的页面 现在我不想有外…

Executor框架的两级调度模型

Executor框架的两级调度模型 在HotSpot VM的线程模型中Java线程(java.lang.Thread)被一对一映射为本地操作系统线程。Java线程启动时会创建一个本地操作系统线程;当该Java线程终止时,这个操作系统线程也会被回收。操作系统会调度…

网络io、io多路复用select/poll/epoll、基于事件驱动的reactor

一、网络IO请求 网络I/O请求是指在计算机网络中,向其他主机或服务器发送请求或接收响应的操作。这些请求可以包括获取网页、下载文件、发送电子邮件等。网络I/O请求需要使用合适的协议和通信方式来进行数据传输,例如HTTP、FTP、SMTP等。 要完成一个完整…

【SSH】在VScode远程开发 使用SSH远程连接服务器

文章目录 前言视频教程1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 转…

实训小总结

1. Web概述 1.1 Web和JavaWeb的概念 Web是全球广域网,也成为万维网,能够通过浏览器访问网站。 在我们日常的生活中,经常会使用浏览器去访问 百度 、 京东 、等这些网站,这些网站统称为Web网站。我们知道了什么是Web,…

超详细的HTML学习笔记:语法特点、骨架结构、基本标签及其属性(附代码示例)

作者想先说一点废话:这篇文章是作者通过学习动力节点、黑马程序员的两套不同的THML课程整理出的学习笔记,融合了两套课程的知识点,耗费了372行代码(加代码注释),希望能帮到大家。HTML只是前端学习的开端&am…

【Redis】聊一下Redis切片集群的原理

背景 在分布式领域中,为了提高系统的稳定性,一般会采用数据复制/镜像的方式,同时部署多个相同功能的节点提供服务,也就是A B C存储的相同的数据。有一个主节点提供读写服务,另外两个节点进行数据的复制,在…

chatgpt赋能python:Python去重-如何高效地处理重复数据

Python去重 - 如何高效地处理重复数据 在数据处理过程中,重复数据可能会导致很多问题,如降低计算效率、影响数据质量等。因此,数据去重是一个非常重要的任务,特别是在大数据处理中更是如此。Python作为一种流行的编程语言&#x…

make xxx_deconfig过程

在uboot中,所写的shell脚本:mx6ull_alientek_emmc.sh的内容如下: #!/bin/bash2 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- distclean3 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig4 make V1…

[数据结构习题]队列——用栈实现队列

[数据结构习题]队列——用栈实现队列 👉知识点导航💎:【数据结构】栈和队列 👉[王道数据结构]习题导航💎: p a g e 85.3 page85.3 page85.3 本节为栈和队列的综合练习题 题目描述: &#x1f…

chatgpt赋能python:Python实现CSV文件只取某两列的方法详解

Python实现CSV文件只取某两列的方法详解 介绍 CSV是一种常见的数据格式,通常使用逗号或分号分隔不同的字段。在处理CSV文件时,我们经常需要只提取其中的某些列,以便进行进一步的分析或处理。使用Python语言,可以很方便地实现这一…

MTK 相机功耗分析流程

和你一起终身学习,这里是程序员Android 经典好文推荐,通过阅读本文,您将收获以下知识点: 一、硬件功耗二、相机软件功耗三、参考文档 一、硬件功耗 1.1 硬件信息 以下硬件信息最好提前获取到 模块备注平台MTK or Qcom or sprdCPU频率大中小核…

【Python】Python系列教程-- Python3 列表(十二)

文章目录 前言访问列表中的值更新列表删除列表元素Python列表截取与拼接嵌套列表列表比较Python列表函数&方法 前言 往期回顾: Python系列教程–Python3介绍(一)Python系列教程–Python3 环境搭建(二)Python系列…

第一次使用Arduino IDE(mac os) 配置合宙ESP32C3(9.9包邮)且烧录代码的历程

目录 Arduino 配置ESP32 1. Arduino 请更新至最新版 2.科学上网 3.添加开发板管理URL 配置 1.连接开发板 2.Arduino IDE 的配置 3.烧录代码 Arduino 配置ESP32 1. Arduino 请更新至最新版 2.科学上网 3.添加开发板管理URL 首选项,编辑并添加 https://…

医疗实施-DRG基本知识

医疗实施-DRG基本知识 DRG常见名词定义: DRG(Diagnosis Related Groups):疾病诊断相关分组,是用于衡量医疗服务质量效率以及进行医保支付的一个重要工具。DRG 实质上是一种病例组合分类方案,即根据年龄、疾病诊断、合…

网络安全学术顶会——NDSS 2023 议题清单、摘要与总结(下)

51、Let Me Unwind That For You: Exceptions to Backward-Edge Protection 通过堆栈缓冲区溢出进行反向边控制流劫持是软件利用的终极目标。直接控制关键的堆栈数据和劫持目标使得攻击者特别喜欢这种利用策略。因此,社区已经部署了强大的反向边保护,如影…

Executor框架的成员

Executor框架的成员 (1)ThreadPoolExecutorThreadPoolExecutor通常使用工厂类Executors来创建。Executors可以创建3种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadPool和CachedThreadPool。1) FixedThreadPool 。…

chatgpt赋能python:Python取某几行-掌握技巧提高效率

Python取某几行-掌握技巧提高效率 Python是一种简单易学、高效编程的语言。它也是一种非常强大的语言,适用于许多不同领域的应用程序。在处理文本文件和数据集时,Python的优势变得尤为突出。在这篇文章中,我们将重点介绍如何使用Python取某几…

Linux【网络编程】之深入理解UDP协议

Linux【网络编程】之深入理解UDP协议 一、传输层二、再谈端口号2.1 端口号划分 三、查看网络状态---netstat四、查看服务器进程ID---pidof五、UDP协议端格式5.1 理解报头 六、UDP的特点七、UDP的缓冲区问题八、常见基于UDP的应用层协议 在前面的几篇文章中我主要偏向于应用层介…

30分钟!从0到1,用ChatGPT+Python做一个AI起名网!

坚持6年,第629篇原创 现在利用ChatGPT可以做很多很多事情,而对于我们程序员来说,是机会也是挑战!因为原来很多工种,很多技术问题现在可以用非常廉价的技术去获取,成本更低了! 打个比方&#xff…