什么是死锁?自己动手写死锁。一个死锁的例子。死锁的四个条件,如何避免死锁。

2023-05-16

文章目录

    • 一、什么是死锁
    • 二、动手写死锁
    • 三、发现排查死锁情况
    • 四、解决办法

面试题就好像计算机二级一样扯淡的出一些并不常见的题目(甚至写法很反人类),我们就借此锻炼一下逻辑能力把。

一、什么是死锁

死锁不仅在个人学习中,甚至在开发中也并不常见。但是一旦出现死锁,后果将非常严重。
首先什么是死锁呢?打个比方,就好像有两个人打架,互相限制住了(锁住,抱住)彼此一样,互相动弹不得,而且互相欧气,你不松手我就不松手。好了谁也动弹不得。
在多线程的环境下,势必会对资源进行抢夺。当两个线程锁住了当前资源,但都需要对方的资源才能进行下一步操作,这个时候两方就会一直等待对方的资源释放。这就形成了死锁。这些永远在互相等待的进程称为死锁进程。

那么我们来总结一下死锁产生的条件:

  1. 互斥:资源的锁是排他性的,加锁期间只能有一个线程拥有该资源。其他线程只能等待锁释放才能尝试获取该资源。
  2. 请求和保持:当前线程已经拥有至少一个资源,但其同时又发出新的资源请求,而被请求的资源被其他线程拥有。此时进入保持当前资源并等待下个资源的状态。
  3. 不剥夺:线程已拥有的资源,只能由自己释放,不能被其他线程剥夺。
  4. 循环等待:是指有多个线程互相的请求对方的资源,但同时拥有对方下一步所需的资源。形成一种循环,类似2)请求和保持。但此处指多个线程的关系。并不是指单个线程一直在循环中等待。

什么?还是不理解?那我们直接上代码,动手写一个死锁。

二、动手写死锁

根据条件,我们让两个线程互相请求保持。

public class DeadLockDemo implements Runnable{

    public static int flag = 1;

    //static 变量是 类对象共享的
    static Object o1 = new Object();
    static Object o2 = new Object();

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":此时 flag = " + flag);
        if(flag == 1){
            synchronized (o1){
                try {
                    System.out.println("我是" + Thread.currentThread().getName() + "锁住 o1");
                    Thread.sleep(3000);
                    System.out.println(Thread.currentThread().getName() + "醒来->准备获取 o2");
                }catch (Exception e){
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println(Thread.currentThread().getName() + "拿到 o2");//第24行
                }
            }
        }
        if(flag == 0){
            synchronized (o2){
                try {
                    System.out.println("我是" + Thread.currentThread().getName() + "锁住 o2");
                    Thread.sleep(3000);
                    System.out.println(Thread.currentThread().getName() + "醒来->准备获取 o2");
                }catch (Exception e){
                    e.printStackTrace();
                }
                synchronized (o1){
                    System.out.println(Thread.currentThread().getName() + "拿到 o1");//第38行
                }
            }
        }
    }

    public static  void main(String args[]){

        DeadLockDemo t1 = new DeadLockDemo();
        DeadLockDemo t2 = new DeadLockDemo();
        t1.flag = 1;
        new Thread(t1).start();

        //让main线程休眠1秒钟,保证t2开启锁住o2.进入死锁
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t2.flag = 0;
        new Thread(t2).start();

    }
}

代码中,
t1创建,t1先拿到o1的锁,开始休眠3秒。然后
t2线程创建,t2拿到o2的锁,开始休眠3秒。然后
t1先醒来,准备拿o2的锁,发现o2已经加锁,只能等待o2的锁释放。
t2后醒来,准备拿o1的锁,发现o1已经加锁,只能等待o1的锁释放。
t1,t2形成死锁。

我们查看运行状态,
在这里插入图片描述

三、发现排查死锁情况

我们利用jdk提供的工具定位死锁问题:

  1. jps显示所有当前Java虚拟机进程名及pid.
  2. jstack打印进程堆栈信息。

在这里插入图片描述
列出所有java进程。
我们检查一下DeadLockDemo,为什么这个线程不退栈。

jstack 11170

在这里插入图片描述

我们直接翻到最后:已经检测出了一个java级别死锁。其中两个线程分别卡在了代码第38行和第24行。检查我们代码的对应位置,即可排查错误。此处我们是第二个锁始终拿不到,所以死锁了。
在这里插入图片描述

四、解决办法

死锁一旦发生,我们就无法解决了。所以我们只能避免死锁的发生。
既然死锁需要满足四种条件,那我们就从条件下手,只要打破任意规则即可。

  1. (互斥)尽量少用互斥锁,能加读锁,不加写锁。当然这条无法避免。
  2. (请求和保持)采用资源静态分配策略(进程资源静态分配方式是指一个进程在建立时就分配了它需要的全部资源).我们尽量不让线程同时去请求多个锁,或者在拥有一个锁又请求不到下个锁时,不保持等待,先释放资源等待一段时间在重新请求。
  3. (不剥夺)允许进程剥夺使用其他进程占有的资源。优先级。
  4. (循环等待)尽量调整获得锁的顺序,不发生嵌套资源请求。加入超时。

参考资料:

https://www.cnblogs.com/xrq730/p/4853713.html
https://baike.baidu.com/item/死锁
http://www.cnblogs.com/baizhanshi/p/5437933.html
https://www.cnblogs.com/Jessy/p/3540724.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是死锁?自己动手写死锁。一个死锁的例子。死锁的四个条件,如何避免死锁。 的相关文章

  • mac/linux 系统批量计算文件md5命令

    find type f print0 xargs 0 md5
  • android 电池充电状态记录

    摘抄源码记录下 http androidxref com 9 0 0 r3 xref frameworks native include batteryservice BatteryServiceConstants h This file
  • python 输入三个变量,然后按小到大输出(解析)

    python 实例解析 xff08 1 xff09 vim 2 python py x 61 int input 39 please input x 39 y 61 int input 39 please input y 39 z 61 i
  • Linux系统调用Hook姿势总结

    http www cnblogs com LittleHann p 3854977 html 主题 Linux 相关学习资料 http xiaonieblog com post 61 121 http hbprotoss github io
  • 利用WireShark进行DNS协议分析

    一 准备工作 系统是Windows 8 1Pro 分析工具是WireShark1 10 8 Stable Version 使用系统Ping命令发送ICMP报文 二 开始工作 打开CMD exe键入 ping www oschina net
  • DNS协议详解及报文格式分析

    DNS协议详解及报文格式分析 Posted on 2017 06 18 by Jocent No Comments 目录 一 DNS协议理论知识 1 1 域名结构1 2 域名服务器1 3 域名解析过程 二 DNS协议报文格式 2 1 头部2
  • 给springboot加项目名

    1 首先需要在application properties配置文件中加 server servlet context path 配置成功后再浏览器上的上输入localhost 8080 即可访问 2 在使用vue等js插件的时 xff0c
  • 命令行输出的内容重定向保存到文件里

    文章目录 1 linux2 windows 1 linux 执行命令 xff0c 不管是python还是c 43 43 xff0c 只要是输出在命令行里的 xff0c 就可以使用重定向把输出的内容保存到一个文件里 例如 xff1a pyth
  • android studio unresolved reference:xx问题解决方案汇总

    android studio unresolved reference xff1a xx问题解决方案汇总 目录第一种 xff0c 重启法 xff0c clean 43 invalidate caches restart第二种 xff0c g
  • Router 选择

    Connected Dominating Set Example of a Connected Dominating Set Router 必须形成一个 CDS xff08 Connected Dominating Set xff0c 连接
  • devtool: unset _PYTHON_SYSCONFIGDATA_NAME

    问题 在 Ubuntu 20 04 1 LTS 上进行编译Yocto时报错 xff0c 出现如下错误 xff1a bb data smart ExpansionError Failure expanding variable SRCPV e
  • VsCode 配置PySide6及测试

    目录 VSCode插件安装安装Python插件安装PySide6插件 PySide6安装PySide6配置VSCode创建UI文件 在这里插入图片描述 https img blog csdnimg cn cbf7cd76d7d84048ab
  • Ubuntu 14.04 Desktop的Raid1安装总结

    Ubuntu 14 04 Desktop的Raid1安装总结 安装基于Ubuntu14 04 Desktop的Raid1 由于采用UEFI GPT方式作为系统启动方式 xff0c 在安装过程中出现了很多异常情况 本文记录安装的过程 安装步骤
  • sem_wait sem_post信号量操作进本函数

    sem wait sem post 信号量的数据类型为结构sem t xff0c 它本质上是一个长整型的数 函数sem init xff08 xff09 用来初始化一个信号量 它的原型为 xff1a extern int sem init
  • 常见gcc编译警告整理(开始)

    1 warning no newline at end of file 在文件最后一行加上回车键 解释 xff1a 在 Rationale for the C99 standard 一文中 xff0c 有C99的相关信息 xff1a A b
  • 对于结构体变量赋值的误区

    以前在使用结构体时没有在结构体变量之间直接赋值 xff0c 今天同事在查看别人的代码时 xff0c 发现有两个结构体变量直接赋值的语句当时感觉这个语句不对 xff0c 认为在一个结构体里边 xff0c 既有一般的无符号整形与数组 xff0c
  • 线程同步(互斥锁与信号量的作用与区别)

    信号量用在多线程多任务同步的 xff0c 一个线程完成了某一个动作就通过信号量告诉别的线程 xff0c 别的线程再进行某些动作 xff08 大家都在semtake的时候 xff0c 就阻塞在 哪里 xff09 而互斥锁是用在多线程多任务互斥
  • 误解程序运行(从单片机到开始)

    误解程序运行 从单片机到开始 关于程序的执行 xff0c 以前想的不多 xff0c 没有意识到一个程序在运行时 xff0c 从哪里读指令 xff0c 数据又写在哪里 最近在看CSAPP时这个念头经常在脑袋中晃荡 从单片机上知道 xff0c
  • ssh配置key后提示Server refused our key

    文章目录 1 确认问题2 解决问题 1 确认问题 参考 xff1a Putty Getting Server refused our key Error 为了确认ssh具体的错误 xff0c 可以去 etc ssh sshd config中
  • Out-of-Bounds Memory References and Buffer Overflow

    callee pushl edp save edp on stack movl esp edp pushl ebx save ebx subl 20 esp popl ebx restore ebx popl edp restore

随机推荐

  • 电子信息工程四年学习之思

    毕业后 xff0c 回顾四年学习历程发现 xff0c 当时以为的明白 xff0c 到现在都是那时的不明白 或许是自己的经历 xff08 参加比赛比较多 xff09 导致了现在的反思 但是 xff0c 回顾那个时候的课程设置 xff0c 却都
  • 将要到来的三大技术革命与联系

    http www csdn net article 2013 02 14 2814128 2013大数据 http www csdn net article 2013 02 15 2814135 bigdata is coming 大数据
  • Keil的常见编译警告

    1 warning 767 D conversion from pointer to smaller integer 解释 xff1a 将指针转换为较小的整数 影响 xff1a 可能造成的影响 xff1a 容易引起数据截断 xff0c 造成
  • 《大数据时代》之后

    现在想想也不记得当时是怎么找到 大数据时代 这本书的 xff0c 好像是在查找数据库方面的书 xff0c 看到亚马逊推荐的书里有这本 xff0c 发现最近才出版的就买一本回来看看 然而这个过程中 xff0c 其实自己已经得到了大数据带来的影
  • 《代码大全》笔记

    最近将去年毕业时 xff0c 大神推荐的 代码大全 看完了 xff08 已经过去一年了 xff0c 要十分感谢推荐 xff0c 还有凤林兄的 深入理解计算机系统 xff09 零零碎碎的时间 xff0c 发现很多东西虽然在书中标记了 xff0
  • 《编程精粹》思之代码与产品

    之前眼中有代码无产品 xff0c 现在眼中有产品有代码 xff0c 什么时候能做到有产品无代码 xff1f 还需要努力 刚开始实习的时候 xff0c 总喜欢在程序中使用 p 43 1 61 而不是p 1 来给入参 xff0c 甚至于用来给定
  • 杂记:pyinstaller 常用参数和可执行文件启动目录的注意事项

    pyinstaller 常用参数 所谓的常用参数 xff0c 就是指 xff1a 不想看详细帮助不关心版本 一来 pip list 就能知道版本 xff0c 不需要专门记 pyinstaller 的参数 二来能用就行 达到生成 exe 的目
  • 关于spring核心配置文件中的各项主要配置

    1 xff1a spring的核心配置文件中的各种配置 spring的核心配置文件的名字 叫做 applicationContext xml xff0c 后期也可以通过配置文件中的配置修改名称 xff0c 在web xml中进行如下配置 x
  • linux安装pgadmin4--postgresql管理工具

    目录 目录 一 下载二 安装三 配置四 总结声明 pip类似RedHat里的yum xff0c 安装起Python包来十分方便 一 下载 1 1 安装pgadmin4 首先确保有python环境 版本均可 xff0c 安装pip span
  • DICOM图像转Nifti格式-SimpleITK

    文章目录 1 SimpleITK实现1 1 代码 2 读图像的meta信息 其实SimpleITK文档里都有 xff0c 直接搬过来 1 SimpleITK实现 1 1 代码 span class token keyword import
  • Linux 压缩(打包)文件夹 tar/zip

    tar 压缩方法 xff1a tar zcvf home xahot tar gz xahot tar zcvf 打包后生成的文件名全路径 要打包的目录 例子 xff1a 把 xahot文件夹打包后生成一个 home xahot tar g
  • 软件工程中的图:流程图,数据流图

    最近在学习这方面的知识 为了可以不必一辈子做程序员 必须学会更多的知识 感谢 http blog csdn net haovip123 article details 19128253 的分享 侵删 软件工程中的那些图 xff1a 系统流程
  • 装系统:用老毛桃winPE给移动硬盘装系统(windows):亲测装机成功~

    初稿 xff1a 我先去整理点图片 资料 xff1a 稍后补全 女朋友笔记本换了个固态 xff0c 替下一个老机械硬盘来 xff0c 正好手头上少win系统 xff0c 遂尝试改装成移动硬盘 43 win系统 xff1a 准备工作 xff1
  • firefox浏览器一分钟去广告--去广告插件安装教程(adblock plus)

    一键去广告 ABP adblock plus https adblockplus org 被广告弄得搓火吗 xff1f 被跟随鼠标的标语弄得烦心吗 xff1f 被横幅弄得感到厌恶吗 xff1f 立即安装 Adblock Plus 来重新取得
  • 骇客代码雨!linux终端黑客帝国数字流效果一分钟教程

    黑客帝国那么炫酷的代码雨怎么来的 xff1f 我怎么能做到 xff1f hack 代码雨在自己终端上实现 xff1a 代码雨 先上效果图 xff1a 开始教程 xff1a 执行 sudo apt get install cmatrix cm
  • % is not in the sudoers file 问题解决:非root用户使用sudo报错

    概述 xff1a 在使用Redhat系列 centOS 等系统时 xff0c 使用非root用户执行sudo可能会遇到 NAME is not in the sudoers file 问题 这是由于这两类系统并没有把非root用户默认的添加
  • Postgresql备份与还原命令pg_dump

    postgresql数据库的备份和还原命令pg dump 常用命令 xff1a 备份 xff1a pg dump span class hljs attribute U span postgres span class hljs attri
  • PostgreSQL如何为主键创建自增序列(Sequences)

    引言 在持久层框架如Hibernate JPA Mybatis中经常会用到Sequences 函数 去创建主键值 xff0c PostgreSQL中 xff0c 用serial数据类型的主键 xff0c 数据库会自动创建Sequences
  • Java中Integer的最大值和最小值

    Java中Integer的最大值和最小值 你为什么会带着这样的问题来这里找答案 java lang Integer Modifier and TypeConstant FieldValuepublic static final intMAX
  • 什么是死锁?自己动手写死锁。一个死锁的例子。死锁的四个条件,如何避免死锁。

    文章目录 一 什么是死锁二 动手写死锁三 发现排查死锁情况四 解决办法 面试题就好像计算机二级一样扯淡的出一些并不常见的题目 甚至写法很反人类 xff0c 我们就借此锻炼一下逻辑能力把 一 什么是死锁 死锁不仅在个人学习中 xff0c 甚至