死锁(面试常问)

1.什么是死锁

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

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

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

java 复制代码
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();
}

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

  1. 多个线程,多把锁。

一个典型模型就是哲学家就餐问题,每个哲学家只会做两件事1.思考人生,啥也不干,阻塞等待;2.吃意大利面,先拿起左手的筷子,再拿起右手筷子。

如图,两个哲学家中间放一根筷子,当所有哲学家都拿起左边筷子时,想要再拿右边筷子,发现没筷子拿了,造成死锁。

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这就导致循环依赖。如果我们给锁编号,约定加多个锁的时候,必须先加编号小的锁,后加编号大的锁,就能有效避免循环等待了。

相关推荐
银发控、14 小时前
数据库隔离级别与三个问题(脏读、不可重复读、幻读)
数据库·面试
JavaGuide14 小时前
微信面试:什么是一致性哈希算法?适用什么场景?
后端·面试
茶杯梦轩15 小时前
从零起步学习并发编程 || 第九章:Future 类详解及CompletableFuture 类在项目实战中的应用
服务器·后端·面试
敲敲了个代码15 小时前
[特殊字符] 空数组的迷惑行为:为什么 every 为真,some 为假?
前端·javascript·react.js·面试·职场和发展
__不想说话__16 小时前
前端开发者的 AI 时代生存指南:大模型如何重塑岗位要求与技能
前端·人工智能·面试
SuperEugene16 小时前
浏览器存储:localStorage / sessionStorage / cookie 应该怎么用
前端·javascript·面试·浏览器
匠心网络科技18 小时前
JavaScript进阶-ES6 带来的高效编程新体验
开发语言·前端·javascript·学习·面试
莫寒清18 小时前
Mybatis的插件原理
面试·mybatis
诚思报告YH19 小时前
视频面试软件市场洞察:2026 - 2032年复合年均增长率(CAGR)为10.3%
面试·职场和发展
绝无仅有19 小时前
计算机网络核心面试知识深入解析
后端·面试·架构