关于我对线程安全问题中死锁的理解

news2024/12/29 0:35:13

文章目录

  • 1.什么是死锁
  • 2.三个典型情况
  • 3.可重入与不可重入
  • 4.死锁的四个必要条件
  • 5.如何破除死锁

1.什么是死锁


比如张三谈了一个女朋友,张三就对这个女朋友加锁了。
此时李四也看上了这个女生,但是他只能等待张三分手(解锁)后,才能和这个女生谈恋爱。

李四为了等待这个女生,错过了好多喜欢他的人,这里就相当于线程无法执行的后续工作,
此时就相当于是死锁了。

一旦程序出现死锁,就会导致线程无法执行后序工作了,此时程序必然会有严重的 bug 。
发生死锁的概率又是随机的,因此死锁是非常隐蔽,不容易被发现的。

2.三个典型情况

情况1:

一个线程如果有一把锁,连续加锁两次。如果这个锁是不可重入锁,就会死锁。

java 中的 synchronizedReentrantLock 都是可重入锁,
因此这一种情况演示不了。


情况2:

两个线程两把锁,t1 和 t2 各自先针对 锁1 和 锁2 加锁,之后再尝试获取对方的锁。

比如说,张三的车钥匙锁在屋里了,而屋子的钥匙锁在车了。
这个时候屋子和车都进不去了,就会产生问题。

下面来举例说明。

package thread;

public class ThreadDemo16 {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (locker1) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (locker2) {
                    System.out.println("线程t1拿到两个锁");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (locker2) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locker1) {
                    System.out.println("线程t2拿到两个锁");
                }
            }
        });
        t1.start();
        t2.start();
    }
}


这里并没有输出结果,说明线程并没有拿到两把锁。

这个时候可以使用 jconsole 来查看当前的进程的情况。


按照这样的路径查找 jconsole ,然后双击。



看到这样的窗口,双击选中的进程。






红色框框的表示 获取锁获取不到的阻塞状态
绿色框框的表示 发生错误的代码行数






情况3: 多个线程,多把锁。(向较与情况2的一半情况

例子:哲学家就餐问题



每个哲学家有两种状态:

  1. 思考人生(相当于线程的阻塞状态
  2. 拿起筷子吃面条(想当于线程获取到锁然后执行一些操作)、

由于操作系统的随机调度,这五个哲学家,随时都可能想吃面条,也随时可能要思考人生。

如果想吃面条就需要拿起左手和右手的筷子。


假如同一时刻,所有的哲学家都拿起左手的筷子吃面条。
此时如果要成功吃到面条,就要等到右边的哲学家放下手中的筷子,自己才可以吃到面条。
此时就会死锁!!!

只有一只筷子没有办法吃面条,必须要等到右边的老铁放下筷子才可以吃。
如果右边一直不放,左边的老铁就一直吃不到。

3.可重入与不可重入

一个线程针对同一个对象,如果不会发生问题就叫可重入的,否则就叫不可重入的。

class Counter {
    public int count = 0;

    synchronized public void add() {
        synchronized(this) {
            count++;
        }
    }
}

锁对象是 this ,只要有线程调用 add 方法,进入 add 方法的时候,
在可以加锁成功的前提下,就会先加锁。紧接着又遇到了代码块,此时会再次尝试加锁。

站在 锁对象的视角(this),他认为自己已经被其他的线程给占用了,
那么这里的第二次加锁是不需要阻塞等待的。

如果允许上述操作,这个锁就是可重入的;不允许就是不可重入的。
如果是不可重入的,就会发生 死锁。

上面演示的就是不可重入的死锁。


下面演示的是可重入的思索。

java 为了避免不小心出现闭锁现象,就把 synchronized 给设置成可重入的了。
因此 java 中才会无法演示上面的情况1.

4.死锁的四个必要条件

1、互斥使用 — 线程1拿到了锁,线程1就需要等待着。

2、不可抢占 — 线程1拿到锁之后,如果线程1不释放锁,线程2就不能强行获取。

3、请求和等待 — 线程1获取到锁A之后,再去获取到锁B,
此时锁A还会继续被线程1获取。(不会因为获取锁B后就把锁A给释放了)

4、循环等待 — 线程1尝试获取到锁A锁B,线程2尝试获取到锁B锁A
线程1在获取B的时候等待线程2释放B,同时线程2在获取A的时候等待线程1释放A

5.如何破除死锁

打破循环等待这个必要条件。

解决办法:

给每个筷子编号,指定固定的顺序(从小到大)拿筷子。


上图是规定从小到大的拿。


到最后一个老铁拿的时候,会拿一号筷子。
但是这个一号筷子被其他的老铁拿了,此时这个老铁就发生阻塞等待了。
此时拿四号筷子的老铁会把五号筷子也拿了,之后开始吃面条。


这个老铁吃面条的时候,拿三号筷子的老铁就会看着他吃。
等待这个老铁吃完,放下两支筷子号筷子,三号筷子的老铁就可以拿起四号筷子来吃了。


按照这样的方式,所有的老铁都可以吃面条。


下面由代码来演示:

package thread;

public class ThreadDemo16 {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();

        //给两个锁编号:1 、2,规定locker1是1号,locker2是2号,按照从小到大的顺序拿
        Thread t1 = new Thread(() -> {
            //先拿序号小的
            synchronized (locker1) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //后拿序号大的
                synchronized (locker2) {
                    System.out.println("线程t1拿到两个锁");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (locker1) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locker2) {
                    System.out.println("线程t2拿到两个锁");
                }
            }
        });
        t1.start();
        t2.start();
    }
}



这种方法是解决死锁,最简单最可靠的方法。

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

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

相关文章

[附源码]计算机毕业设计保护濒危动物公益网站Springboot程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

赋能千百行加快 “5G+工业互联网”落地深耕

目前,我国5G基站已达196.8万个,“5G工业互联网”建设项目3100个,5G与工业互联网融合应用深入工业设计、制造、管理、服务等各环节,在促进传统企业提质降本增效的同时,不断拓展信息通信产业新蓝海。 记者从工信部获悉&a…

Nginx部署Vue前端项目

系统环境:Mac Pro—10.15.7版本 Nginx版本:1.19.6 一、安装Nginx brew install nginx 1、安装完成 Nginx的配置文件目录:/usr/local/etc/nginx Nginx的安装目录:/usr/local/Cellar/nginx 2、启动Nginx brew services star…

Linux虚拟机共享文件夹不显示问题终极解决方法

打开共享文件夹处查看一下,我的结果如下图(没有显示共享文件内容): 接着打开终端: 输入命令:没有共享目录显示 解决方法: 输入:sudo apt-get install open-vm-tools 等待安装完 …

音乐在线教育解决方案,打造在线教育高品质教学体验

音乐是一种在时间中流动的形态,有节奏的音乐,能给人带来心理的快乐和艺术的享受,学一门乐器成为了很多人的选择。因此,音乐教育培训机构在搭建线上音乐课程时,就需要选择低延时、高同步、画质还原、高保真的技术方案&a…

基于轻量级CNN的12306验证码识别分析系统

在我很早的一篇文章中有写过图标型验证码识别的实践项目,这里主要是基于以往的实践经历做出的技术升级,包括:模型轻量化、界面开发、Grad-CAM热力图集成等。 话不多说,首先看效果: 基于CNN的轻量级12306验证码识别分析…

用Python预测世界杯球赛结果,还别说准确度还是蛮高的

前言 那么四年一度的世界杯即将要在卡塔尔开幕了,对于不少热爱足球运动的球迷来说,这可是十分难得的盛宴,而对于最后大力神杯的归属,相信很多人都满怀着期待,每个人心中都有不同的答案。 今天我就通过Python数据分析…

Word控件Spire.Doc 【图像形状】教程(10): 如何在C#中重置word文档的形状大小

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下,轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具,专注于创建、编辑、转…

SEO优化之扁平化目录结构PbootCMS

在网站内部优化中,扁平化的目录结构对于一个网站来讲是非常有意义的。我们经常看到一些网站结构混乱,搞的你都不知道哪里是哪里了。而有的网站结构非常清晰,这样用户喜欢,搜索引擎也喜欢。而PbootCMS 2.0开始,系统也默…

2022,开发转测试,会不会后悔?

每个人做任何选择都是趋利避害的,当选择了认为对自己更有利的选择后,发现结果和期望不符,甚至发现大失所望,才会出现后悔的情绪。 那么为什么开发转测试就会后悔呢,只有一个原因: 那就是转到测试行业后&a…

PIL+pyplot+transforms.ToTensor+unsqueeze+div

目录PIL & pyplottransforms.ToTensortorch.unsqueeze(input,dim,outNone)torch.squeeze(input, dimNone, outNone)torch.divPIL & pyplot from PIL import Image import matplotlib.pyplot as plt from torchvision import transforms# PIL打开图片 img_PIL Image.o…

面试华为必备:华为18级技术官呕心沥血三年整理的 趣谈网络协议

华为是一个热门的互联网大厂,华为每年都要进很多新人,对于新人来说,那些高大上的技术是用不到的,反倒是非常需要这些基础的文档,有这些新手进步会快很多,搞清楚TCP IP协议栈,其他的学起来也就容…

运动规划问答 -路径规划

1、Dijstra算法,算法流程 关键 初始化: 算法是基于图搜索的思想,所以需要先构建一个图,图中不包含障碍物,读入起点和终点信息,设置一个以代价作为键值排序的multiMap,起点入队,并设…

搜索技术——遗传算法

如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:瞳孔空间 一:进化与遗传的概念 拉马克(Lamarck)进化论: 一切物种都是其他物种演变和进化而来的,而生物的演变和进化是一个缓慢和连续的过程环境的变化能够引起生…

Qwt开发笔记(一):Qwt简介、下载以及基础demo工程模板

若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/128146985 红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…

设计模式之观察者模式(十四)

目录 1. 背景 1.1 天气预报项目 2. 观察者模式 2.1 观察者模式解决天气预报项目 2.2 观察者模式在JDK中应用 1. 背景 1.1 天气预报项目 天气预报项目需求,具体要求如下: 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去…

ASEMI整流桥KBL406参数,KBL406规格,KBL406封装

编辑-Z ASEMI整流桥KBL406参数: 型号:KBL406 最大重复峰值反向电压(VRRM):600V 最大RMS电桥输入电压(VRMS):420V 最大直流阻断电压(VDC):600…

完全免费、开源的Flutter架构来了;开发者做好准备了吗?

Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面 一份代码可以同时生成 iOS 和 Android 两个高性能、高保真的应用程序 Flutter 目标就是使开发人员能够交付在不同平台上都感觉自然流畅的高性能应用程序,并且兼容 滚…

萤火虫模糊回归算法(Matlab代码实现)

🍒🍒🍒欢迎关注🌈🌈🌈 📝个人主页:我爱Matlab 👍点赞➕评论➕收藏 养成习惯(一键三连)🌻🌻🌻 🍌希…

FA-Phe-Gly-Gly,64967-39-1

Substrate for a continuous spectrophotometric assay of human angiotensin I-converting enzyme (ACE). Enzymatic cleavage of FAPGG yields FA-Phe-OH and H-Gly-Gly-OH. 连续分光光度法测定人血管紧张素i转换酶(ACE)的底物。酶法裂解FAPGG产生fa - pheo - oh和H-Gly-Gly-…