死锁(面试常问)

news/2024/5/20 14:20:03 标签: 面试, 死锁

1.什么是死锁

简单来说就是一个线程加锁后解锁不了

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

举几个例子,1.钥匙锁车里了,车钥匙锁家里了。2. 现在有一本书和一支笔,A拿到书,B拿到笔;A说你把笔给我,我用完再把书给你;B说你把书给我,我用完笔给你。这个场景就相持不下了。

public static void main(String[] args) {
	Object locker1 = new Object();
	Object locker2 = new Object();
	Thread t1 = new Thread(()-> {
		synchronized (locker1) {
			System.out.println("t1线程获取Locker1");
			synchronized (locker2) {
				System.out.println("t1线程获取locker2");
			}
		}
	});
	Thread t2 = new Thread(() -> {
		synchronized (locker2) {
			System.out.println("t2线程尝试获取locker2");
			synchronized (locker1) {
				System.out.println("t2线程尝试获取locker1");
			}
		}
	});
	t1.start();
	t2.start();
}

image.png
上面代码就是两线程,两个锁造成了死锁

  1. 多个线程,多把锁。

一个典型模型就是哲学家就餐问题,每个哲学家只会做两件事1.思考人生,啥也不干,阻塞等待;2.吃意大利面,先拿起左手的筷子,再拿起右手筷子。
image.png
如图,两个哲学家中间放一根筷子,当所有哲学家都拿起左边筷子时,想要再拿右边筷子,发现没筷子拿了,造成死锁

2. 出现死锁的四个必要条件

  1. 互斥,锁A被线程1占有,线程2就没办法占有
  2. 不可抢占,锁A被线程1占有,线程2不能直接把锁A抢过来,阻塞等待
  3. 请求和保存,有多把锁,线程1拿到锁A之后,不想释放锁还想拿锁B
  4. 循环等待,线程1等待线程2释放锁,线程2等待线程3释放锁,线程3等待线程1释放锁

3.避免死锁的方案

只要打破上面四个必要条件任意一个即可解决。由于互斥和不可抢占是内核决定的无法改变。打破请求和保持,适用场景不多,要看需要场景是否允许。
打破循环依赖,约定好加锁顺序,就可以打破。像t1线程加锁顺序是locker1,locker2;t2线程加锁顺序是locker2,locker1这就导致循环依赖。如果我们给锁编号,约定加多个锁的时候,必须先加编号小的锁,后加编号大的锁,就能有效避免循环等待了。


http://www.niftyadmin.cn/n/5252958.html

相关文章

Dockerfile 指令的最佳实践

这些建议旨在帮助您创建一个高效且可维护的Dockerfile。 一、FROM 尽可能使用当前的官方镜像作为镜像的基础。Docker推荐Alpine镜像,因为它受到严格控制,体积小(目前不到6 MB),同时仍然是一个完整的Linux发行版。 FR…

【C++】使用“/**/“进行注释的好处

2023年12月10日,周日晚上 我今天下午看Google Chrome的源码时,才发现"/**/"原来还能这么用 使用"/**/"的好处就是,可以在任何地方进行注释,哪怕是参数列表 void CircularWindow::enterEvent(QEvent *event/…

【NodeJs】UniSMS 实现短信验证码功能

承接上文 ,上次用的是 短信宝平台 认证已经通过 后续又新增要求 平台相当麻烦! 短信宝实现短信发送要求: 1.平台绑定手机号 必须和 营业执照法人一致 2.平台个人实名认证 必须和 营业执照法人一致 3.平台需要上传营业执照 4.平台需要上…

springboot集成knife4j详细教程

使用原生的swagger作为接口文档,功能不够强大,并且默认的ui比较简陋,不符合大众审美。所以实际开发中推荐使用knife4j对swagger进行增强。knife4j的地址:https://gitee.com/xiaoym/knife4j 基本使用 想要使用knife4j非常简单&…

class067 二维动态规划【算法】

class067 二维动态规划 code1 64. 最小路径和 // 最小路径和 // 给定一个包含非负整数的 m x n 网格 grid // 请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 // 说明:每次只能向下或者向右移动一步。 // 测试链接 : https://leetcode…

MySQL常见的存储引擎(InnoDB、MyISAM)data目录下(.frm,.myi,.myd)

查看支持的存储引擎 SHOW ENGINES;show variables like %storage_engine%; 看你的mysql当前默认的存储引擎:data目录 如果一个表同时有这3个文件,则存储引擎是myisam: (1)*.frm--表定义,是描述表结构的文件。 &#…

Linux Component概述和高通component的使用

1 Linux为什么要引入Component框架? 为了让subsystem按照一定顺序初始化设备才提出来的。 subsystem中由很多设备模块,内核加载这些模块的时间不确定。子系统内有些模块是需要依赖其它模块先初始化才能进行自己初始化工作(例如v4l2 subdev和v4l2 video …

Java编程秘籍:掌握集合与数组的互相转换

文章目录 集合转数组数组转集合 在Java编程中,集合和数组是我们常用的数据结构,它们各有优点,有时我们需要在两者之间进行转换。这篇文章将详细介绍如何在Java中将集合转换为数组,以及如何将数组转换为集合。 集合转数组 使用集…