专项攻克——死锁

文章目录

  • O、死锁定义
  • 一、 常见的java死锁代码
    • 1. synchronized等待对象释放,导致死锁
  • 2. CountDownLatch计数等待,导致死锁
  • 二、怎么避免死锁
    • 2.1 死锁的四个必要条件
    • 2.2 避免死锁
    • 2.3 常见的避免死锁技术
  • 三、java程序出现死锁,怎么解除?
    • 3.1 怎么排查是否有死锁
    • 3.2 死锁已经发生,怎么解除死锁?
  • 四、MySQL死锁怎么解决
    • 4.1 表锁死锁
    • 4.2 行锁死锁
    • 4.3 共享锁转排他锁

O、死锁定义

死锁定义:是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

一、 常见的java死锁代码

1. synchronized等待对象释放,导致死锁

以交易场景为例:商人A把钱拿的死死的,想等商人B把货物发出后再交钱;而商人B把货物拿的死死的,想等商人A把钱交了后才发货。两人处于互相等待释放资源状态,陷入死锁。

两个线程互相等待彼此释放自己所需要的对象,才释放自己锁住的对象,陷入死锁

  • 线程1锁住object1,线程1等待线程2释放object2,才会释放object1
  • 线程2锁住object2,线程2等待线程1释放object1,才会释放object2
/**
 * 死锁案例
 * - 线程1锁住object1,线程1等待线程2释放object2,才会释放object1
 * - 线程2锁住object2,线程2等待线程1释放object1,才会释放object2
 * */
public class DeadLockSynchTest_5 {
    public static void main(String[] args) {
        Object object1 = new Object();
        Object object2 = new Object();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object1) {
                    System.out.println(Thread.currentThread().getName() + ",object1状态:锁住");
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                    synchronized (object2) {//线程1锁住object1,线程1等待线程2释放object2,才会释放object1
                        System.out.println(Thread.currentThread().getName() + ",object2状态:锁住");
                    }
                    System.out.println(Thread.currentThread().getName() + ",object2状态:释放");
                }
                System.out.println(Thread.currentThread().getName() + ",object1状态:释放");
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object2) {
                    System.out.println(Thread.currentThread().getName() + ",object2状态:锁住");
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                    synchronized (object1) {//线程2锁住object2,线程2等待线程1释放object1,才会释放object2
                        System.out.println(Thread.currentThread().getName() + ",object1状态:锁住");
                    }
                    System.out.println(Thread.currentThread().getName() + ",object1状态:释放");
                }
                System.out.println(Thread.currentThread().getName() + ",object2状态:释放");
            }
        }).start();
    }
}

输出结果
在这里插入图片描述

2. CountDownLatch计数等待,导致死锁

/**
 * 死锁案例
 * - 线程1持有资源countDownLatch1,线程1等待countDownLatch2置空,才置空countDownLatch1
 * - 线程2持有资源countDownLatch2,线程2等待countDownLatch1置空,才置空countDownLatch2
 * */
public class DeadLockCountDownLatchTest_5 {
    public static void main(String[] args) {
        CountDownLatch countDownLatch1 = new CountDownLatch(1);
        CountDownLatch countDownLatch2 = new CountDownLatch(1);

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ",等待countDownLatch2置空");
                try {
                    countDownLatch2.await();
                } catch (InterruptedException e) {
                    System.out.println(e);
                }
                System.out.println("countDownLatch2 已置空");
                while(countDownLatch1.getCount() != 0){//释放资源countDownLatch1
                    countDownLatch1.countDown();
                }
                System.out.println("countDownLatch1 已释放");
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ",等待countDownLatch1置空");
                try {
                    countDownLatch1.await();
                } catch (InterruptedException e) {
                    System.out.println(e);
                }
                System.out.println("countDownLatch1 已置空");
                while(countDownLatch2.getCount() != 0){//释放资源countDownLatch2
                    countDownLatch2.countDown();
                }
                System.out.println("countDownLatch2 已释放");
            }
        }).start();
    }
}

输出结果
在这里插入图片描述

二、怎么避免死锁

要想避免或打破死锁,首先得知道死锁的必要条件有哪些,只要打破其中一个就能避免或打破死锁

2.1 死锁的四个必要条件

  1. 互斥:一个资源不能被多个线程同时使用,只能同时被一个线程使用,其他线程只能等待。

  2. 请求并保持:线程在请求资源阻塞的时候,并不会释放其已经拥有的资源。

  3. 不可剥夺:对于线程已经获得的资源,只能线程自己释放,其他线程无法强制剥夺。

  4. 循环等待:两个或者两个以上线程所等待的资源出现循环依赖。

2.2 避免死锁

  1. 破坏“请求和保持”条件

    • 方法:如果进程已经有了资源,就不要去竞争那些不可抢占的资源。比如,进程在申请资源时,一次性申请所有需要用到的资源,不要一次一次来申请,当申请的资源有一些没空,那就让线程等待。
    • 缺点:比较浪费资源,进程可能经常处于饥饿状态。还有一种方法是,要求进程在申请资源前,要释放自己拥有的资源。
  2. 破坏“不可剥夺”条件

    • 方法1:允许进程进行抢占,如果抢资源被拒绝,就释放自己的资源。
    • 方法2:操作系统允许抢,只要你优先级大,可以抢到。
    • 缺点:增加系统开销,且进程前段工作可能失效。
  3. 破坏“循环等待”条件

    • 方法:统一编号系统中的所有资源,进程可在任何时刻提出资源申请,且所有申请必须按照资源的编号顺序(升序)提出。
    • 缺点:限制进程对资源的请求,系统开销大。

2.3 常见的避免死锁技术

  • 常见的避免死锁方式:

    1. 加锁顺序(给资源编号,线程按照该资源编号顺序给资源加锁)
    2. 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
    3. 死锁检测
  • 具体步骤:

    1. 每个进程、每个资源制定唯一编号
    2. 设定一张资源分配表,记录各进程与占用资源之间的关系
    3. 设置一张进程等待表,记录各进程与要申请资源之间的关系
  • 典型案例:银行家算法,参见博客【银行家算法】

    知识补充——银行家算法

    1. 一句话:当一个进程申请使用资源的时候,银行家算法通过先 试探 分配给该进程资源,然后通过安全性算法判断分配后的系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待。
    2. 背景:客户向银行申请贷款的额度是有限的,每个客户在第一次申请贷款时要声明“完成该项目所需的最大资金量”,在满足所有贷款要求时,客户应及时归还贷款,银行家在客户申请的贷款额度不超过自己拥有的最大值时,都应尽量满足客户需要。
    3. 例子:假设银行家有10万元资金,有三个客户A、B、C,分别需要8、3、9万元贷款完成项目,客户要求分期贷款,目前A已贷到4万 此时,B要申请2万,C要申请4万 银行家需要评估贷款请求的安全性,避免出现坏账。 B借2万,C借4万,能借吗?在这个例子中银行家是操作系统,资金是资源,客户是要申请资源的进程。
    4. 安全性逻辑判断:假设进程P1申请资源,银行家算法先试探地分配给它(先要看看当前资源池中的资源数量够不够),若申请的资源数量小于等于Available,就接着判断分配给P1后剩余的资源,看能不能使进程队列的某个进程执行完毕。(1)若没有进程可执行完毕,则系统处于不安全状态(即此时没有一个进程能够完成并释放资源,随时间推移,系统终将处于死锁状态)。(2)若有进程可执行完毕,则假设回收已分配给它的资源(剩余资源数量增加),把这个进程标记为可完成,并继续判断队列中的其它进程。(3)若所有进程都可执行完毕,则系统处于安全状态,并根据可完成进程的分配顺序生成安全序列(如{P0,P3,P2,P1}表示将申请后的剩余资源Work先分配给P0–>回收(Work+已分配给P0的资源A0=Work)–>分配给P3–>回收(Work+A3=Work)–>分配给P2–>······满足所有进程)。
      如此就可避免系统存在潜在死锁的风险。

三、java程序出现死锁,怎么解除?

3.1 怎么排查是否有死锁

- 步骤1、命令"jps":显示所有当前Java虚拟机进程名及pid.

在这里插入图片描述
- 步骤2、命令"jstack 67918" 打印进程堆栈信息,检查一下DeadLockSynchTest_5,为什么这个进程不退栈
在这里插入图片描述

3.2 死锁已经发生,怎么解除死锁?

解除死锁的关键在于释放一部分死锁进程占有的资源,让其他进程能够顺利执行,常见的做法有:
1、资源剥夺法:强行挂起或撤销某些进程,释放这些进程占有的资源
2、进程回退法:让某些进程回退到能够避免死锁的地步,释放被占有的资源,这种方法也增加了实现的难度和复杂度,要求保留执行的历史信息并设置还原点。

四、MySQL死锁怎么解决

4.1 表锁死锁

  • 产生原因:
    用户A访问表A(锁住了表A),然后又访问表B;另一个用户B访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。

    用户A–》A表(表锁)–》B表(表锁)
    用户B–》B表(表锁)–》A表(表锁)

  • 解决方案:
    这种死锁,除了调整程序逻辑,没有其它办法。对于多表操作,尽量按相同顺序进行处理,尽量避免同时锁定两个资源。如,操作A和B两张表时,总是按先A后B的顺序处理,必须同时锁定两个资源时,要保证在任何时刻都应该按照相同顺序锁定资源。

4.2 行锁死锁

  • 产生原因1:
    如果在事务中执行了一条没有索引条件的查询,引发全表扫描,把行级锁上升为全表记录锁定(等价于表级锁)。多个这样的事务执行后,就很容易产生死锁和阻塞,最终应用系统会越来越慢,发生阻塞或死锁。

  • 解决方案1:
    SQL 语句中不要使用太复杂的关联多表的查询;
    使用 explain “执行计划"对 SQL 语句进行分析,对于有全表扫描和全表锁定的 SQL 语句,建立相应的索引进行优化。

  • 产生原因2:
    两个事务分别想拿到对方持有的锁,互相等待,于是产生死锁。

  • 解决方案2:
    (1)在同一个事务中,尽可能做到一次锁定所需要的所有资源;
    (2)按照 id 对资源排序,然后按顺序进行处理。

4.3 共享锁转排他锁

  • 产生原因:
    事务A 查询一条纪录,然后更新该条纪录;此时事务B 也更新该条纪录,这时事务B 的排他锁由于事务A 有共享锁,必须等A 释放共享锁后才可以获取,只能排队等待。事务A 在执行更新操作时,此处发生死锁,因为事务B 已经有一个排他锁请求,并且正在等待事务A 释放其共享锁。

  • 解决方案:
    (1)对于按钮等控件,点击立刻失效,不让用户重复点击,避免引发同时对同一条记录多次操作;
    (2)使用乐观锁进行控制。乐观锁机制避免了长事务中的数据库加锁开销,大大提升了大并发量下的系统性能。注意,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue使用的编辑器

作者简介:一名计算机萌新、前来进行学习VUE,让我们一起进步吧。 座右铭:低头赶路,敬事如仪 个人主页:我叫于豆豆吖的主页 目录 前言 一.vue常用的IDE工具Visual Studio Code 3. 汉化教程 4.常用快捷键 5. Visual Studio C…

瑞萨Renesas RA2L1 开发板测评(1)--keil环境配置

前言(1)首先感谢李肯前辈的活动,从而申请到了RA2L1开发板的测评。(2)本文将会简单介绍此开发的Renesas RA2L1 开发板的前期配置。需要注意的是,MDK版本要5.30 以上。MDK下载链接;(3&…

计算机中的浮点数运算

计算机中的浮点数 计算机中以固定长度存储浮点数的方式&#xff0c;造成了浮点数运算过程容易产生上溢和下溢。以float32为例, 其标记位占1bit,指数位占8bit,小数部分占23bit 经典下溢场景 不满足精度导致截断误差 #include <iostream> #include <iomanip> usin…

一行代码“黑”掉任意网站

文章目录只需一行代码&#xff0c;轻轻一点就可以把任意网站变成暗黑模式。 首先我们先做一个实验&#xff0c;在任意网站中&#xff0c;打开浏览器开发者工具(F12)&#xff0c;在 C1onsole 控制台输入如下代码并回车&#xff1a; document.documentElement.style.filterinve…

Hive 数据倾斜

数据倾斜&#xff0c;即单个节点任务所处理的数据量远大于同类型任务所处理的数据量&#xff0c;导致该节点成为整个作业的瓶颈&#xff0c;这是分布式系统不可能避免的问题。从本质来说&#xff0c;导致数据倾斜有两种原因&#xff0c;一是任务读取大文件&#xff0c;二是任务…

【MIT 6.S081】Lab2: system calls

本Lab包括两个简单系统调用的实现&#xff0c;进一步熟悉系统调用接口。 笔者用时约1.5h 概述 根据文档说明&#xff0c;当我们添加一个系统调用时&#xff0c;比如第一个任务是添加一个trace&#xff0c;需要进行以下操作&#xff1a; 首先将系统调用的原型添加到user/user…

博客系统实现自动化测试

目录 一、设计博客系统的测试用例 二、利用测试用例进行测试 测试登录页面 界面测试 功能测试 测试博客列表页 界面测试 功能测试 测试博客详情页 界面测试 功能测试 博客编辑页测试 界面测试 功能测试 一、设计博客系统的测试用例 二、利用测试用例进行测…

【Java版oj】day12二进制插入、查找组成一个偶数最接近的两个素数

目录 一、二进制插入 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 二、查找组成一个偶数最接近的两个素数 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff0…

One-YOLOv5 v1.2.0 Released(支持分类,检测,实例分割)

0x0. 引言0x1. 快速开始0x2. 在COCO上的精度表现 yolov5s-defaultyolov5s-seg 0x3. 在COCO上的单GPU性能表现特性 & bug 修复 特性用户反馈的bug 下个版本的展望附件常用预训练模型下载列表 0x0. 引言 &#x1f31f; v1.2.0同步了ultralytics yolov5的上游分支v7.0 &…

前端入门:HTML5+CSS3+JAAVASCRIPT

1、 初识HTML HTML:Hyper Text Markup Language(超文本标记语言) 。 超文本包括&#xff1a;文字、图片、音频、视频、动画等。 1.1、W3C标准 1.2、HTML基本结构 示例&#xff1a; <!-- DOCTYPE:告诉浏览器&#xff0c;我们要使用什么规划&#xff0c;这里是HTML --> …

IDAFrida

IDA&Frida 前言 偶然间发现了一本秘籍《IDA脚本开发之旅》&#xff0c;这是白龙的系列文章&#xff0c;主要是安卓平台&#xff0c;笔者只是根据他的知识点学习&#xff0c;拓展&#xff0c;可以会稍微提及别的平台。本文并不会贴出他的思路分析&#xff0c;只对于源码进…

C语言实现单链表(超多配图,这下不得不学会单链表了)

目录 一&#xff1a;什么是链表&#xff1f; 二&#xff1a;创建源文件和头文件 (1)头文件 (2)源文件 三&#xff1a;实参和形参 四&#xff1a;一步步实现单向链表 &#xff08;1&#xff09;建立一个头指针并置空 &#xff08;2&#xff09;打印链表&#xff0c;便于…

*p++,*(p++),*++p,(*p)++区别?

*p++:等同于:*p; p += 1; 解析:由于和++的运算优先级一样,且是右>结合。故p++相当于*(p++),p先与++结合,>然后p++整体再与结合。前面陈述是一种最 常见的错误,很多初学者也是这么理解的。 但是,因为++后置的时候,本身含义就是先 运算后增加1(运算指的是p++作为…

GPT-4最震撼我的一点

昨天我看了一遍OpenAI发的视频和论文&#xff0c;最震撼我的并不是根据手绘草图生成HTML页面代码&#xff0c;因为草图太简单&#xff0c;对于复杂的有交互的界面&#xff0c;还不知道它的能力究竟如何&#xff0c;能不能生成准确的、清晰的代码&#xff0c;我再实验一下再给大…

C实现栈及OJ题有效的括号

文章目录栈概念及基本操作源码OJ题括号匹配栈概念及基本操作 栈也同链表和顺序表一样是一种线性表只是比较特殊而已&#xff0c;栈遵循一种先进后出的原则,其实栈就像生活中的叠盘子一样&#xff0c;将盘子一个一个的叠起来&#xff0c;每次都只能在最顶层叠&#xff0c;然后取…

ElasticSearch快速入门详解(亲测好用,强烈推荐收藏)

3.快速入门 接下来快速看下elasticsearch的使用 3.1.概念 Elasticsearch虽然是一种NoSql库&#xff0c;但最终的目的是存储数据、检索数据。因此很多概念与MySQL类似的。 ES中的概念数据库概念说明索引库&#xff08;indices)数据库&#xff08;Database&#xff09;ES中可…

聊聊「订单」业务的设计与实现

订单&#xff0c;业务的核心模块&#xff1b; 一、背景简介 订单业务一直都是系统研发中的核心模块&#xff0c;订单的产生过程&#xff0c;与系统中的很多模块都会高度关联&#xff0c;比如账户体系、支付中心、运营管理等&#xff0c;即便单看订单本身&#xff0c;也足够的复…

带你了解Redis及安装Redis的全过程

文章目录Redis是什么&#xff1f;官网介绍与传统的数据库的区别优势Redis下载安装Redis①配置gcc②开始安装redisRedis是什么&#xff1f; Redis&#xff1a;REmote Dictionary Server&#xff08;远程字典服务&#xff09;基于内存的Key—Value键值对内存数据库 官网介绍 R…

JVM学习.01 内存模型

1、前言对于C、C程序员来说&#xff0c;在内存管理领域&#xff0c;他们拥有对象的“所有权”。从对象建立到内存分配&#xff0c;不仅需要照顾到对象的生&#xff0c;还得照顾到对象的消亡。背负着每个对象生命开始到结束的维护和管理责任。对于JAVA程序来说&#xff0c;因为J…

第十四届蓝桥杯三月真题刷题训练——第 15 天

目录 第 1 题&#xff1a;斐波那契与7 问题描述 答案提交 运行限制 代码&#xff1a; 第 2 题&#xff1a;小蓝做实验 问题描述 答案提交 运行限制 代码&#xff1a; 第 1 题&#xff1a;斐波那契与7 问题描述 斐波那契数列的递推公式为: FnFn−1Fn−2​, 其中 F1F21…