Update 计算机操作系统 - 死锁.md

调整银行家算法配图中的说明文字,统一列表标点,增加死锁示例的实测附录。
This commit is contained in:
CyC2018
2026-10-28 03:19:07 +08:00
parent 3a3b56ca4b
commit 3765052cb4
2 changed files with 46 additions and 14 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 163 KiB

+46 -14
View File
@@ -20,6 +20,7 @@
* [程序中的死锁](#程序中的死锁)
* [1. Java 线程死锁](#1-java-线程死锁)
* [2. MySQL 死锁](#2-mysql-死锁)
* [附录:实测](#附录实测)
* [参考资料](#参考资料)
<!-- GFM-TOC -->
@@ -28,9 +29,9 @@
<div align="center"> <img src="pics/deadlock-transfer.png" width="740px"/> </div><br>
- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
- 占有和等待:已经得到了某个资源的进程可以再请求新的资源。
- 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的;
- 占有和等待:已经得到了某个资源的进程可以再请求新的资源;
- 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放;
- 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。
## 处理方法
@@ -50,7 +51,7 @@
当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。
大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。
大多数操作系统,包括 Unix、Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。
## 死锁检测与死锁恢复
@@ -72,10 +73,10 @@
上图中,有三个进程三种资源,每个数据代表的含义如下:
- E 向量:资源总量
- A 向量:资源剩余量
- C 矩阵:每个进程所拥有的资源数量,每一行都代表一个进程拥有资源的数量
- R 矩阵:每个进程请求的资源数量
- E 向量:资源总量;
- A 向量:资源剩余量;
- C 矩阵:每个进程所拥有的资源数量,每一行都代表一个进程拥有资源的数量;
- R 矩阵:每个进程请求的资源数量。
进程 P<sub>1</sub> 和 P<sub>3</sub> 所请求的资源都得不到满足,只有进程 P<sub>2</sub> 可以,让 P<sub>2</sub> 执行,之后释放 P<sub>2</sub> 拥有的资源,此时 A = (3 1 2)。P<sub>1</sub> 可以执行,执行后释放 P<sub>1</sub> 拥有的资源,A = (4 2 2)。P<sub>3</sub> 也可以执行。所有进程都可以顺利执行,没有死锁。
@@ -83,8 +84,8 @@
每个进程最开始时都不被标记,执行过程有可能被标记。当算法结束时,任何没有被标记的进程都是死锁进程。
1. 寻找一个没有标记的进程 P<sub>i</sub>,它所请求的资源小于等于 A。
2. 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转回 1。
1. 寻找一个没有标记的进程 P<sub>i</sub>,它所请求的资源小于等于 A;
2. 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转回 1;
3. 如果没有这样一个进程,算法终止。
### 3. 死锁恢复
@@ -143,8 +144,8 @@
检查一个状态是否安全的算法如下:
- 查找 Need 矩阵中是否存在一行小于等于向量 Available。如果不存在这样的行,那么系统将会发生死锁,状态是不安全的。
- 假若找到这样一行,将该进程标记为终止,并将其已分配资源加到 Available 中。
- 查找 Need 矩阵中是否存在一行小于等于向量 Available。如果不存在这样的行,那么系统将会发生死锁,状态是不安全的;
- 假若找到这样一行,将该进程标记为终止,并将其已分配资源加到 Available 中;
- 重复以上两步,直到所有进程都标记为终止,则状态是安全的。
如果一个状态不是安全的,需要拒绝进入这个状态。
@@ -193,7 +194,7 @@ synchronized (first) {
}
```
两个线程各反向转账 10 万次,原来的写法每次运行都会卡住,按 id 顺序加锁后都能完成,两个账户的余额仍为 1000。使用 ReentrantLock 时还可以用 tryLock 设置超时,拿不到锁就释放已持有的锁再重试,破坏的是占有和等待条件。
两个线程各反向转账 10 万次,原来的写法每次运行都会卡住,按 id 顺序加锁后都能完成,两个账户的余额仍为 1000(JDK 21 实测,见[附录:实测](#附录实测))。使用 ReentrantLock 时还可以用 tryLock 设置超时,拿不到锁就释放已持有的锁再重试,破坏的是占有和等待条件。
### 2. MySQL 死锁
@@ -212,7 +213,7 @@ UPDATE account SET balance = balance + 50 WHERE id = 1;
-- ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
```
InnoDB 默认开启死锁检测,发现环后立即回滚其中一个事务(倾向于回滚修改行数少的事务),另一个事务继续执行。上例中事务 2 被回滚,事务 1 提交后余额为 900 和 1100。执行 SHOW ENGINE INNODB STATUS,在 LATEST DETECTED DEADLOCK 一节中可以看到两个事务各自持有和等待的锁。被回滚的事务需要由应用重试。
InnoDB 默认开启死锁检测,发现环后立即回滚其中一个事务(倾向于回滚修改行数少的事务),另一个事务继续执行。上例中事务 2 被回滚,事务 1 提交后余额为 900 和 1100(MySQL 8.0 实测,见[附录:实测](#附录实测))。执行 SHOW ENGINE INNODB STATUS,在 LATEST DETECTED DEADLOCK 一节中可以看到两个事务各自持有和等待的锁。被回滚的事务需要由应用重试。
减少数据库死锁的方法:
@@ -220,6 +221,37 @@ InnoDB 默认开启死锁检测,发现环后立即回滚其中一个事务(
- 事务尽量短,不在事务中调用外部接口或等待用户操作;
- 为更新的条件列建立索引,避免扫描时锁住过多的行。
## 附录:实测
Java 线程死锁:JDK 21,两个线程分别执行 10 万次 transfer(a, b, 1) 和 transfer(b, a, 1),设为守护线程,各等待 3 秒后检查是否结束,各运行 3 次:
```text
原写法:stuck
原写法:stuck
原写法:stuck
按 id 顺序加锁:1000 1000
按 id 顺序加锁:1000 1000
按 id 顺序加锁:1000 1000
```
MySQL 死锁:MySQL 8.0.44,account 表中 id 为 1、2 的两行余额都是 1000,用两个连接按正文的顺序执行,最后执行 SHOW ENGINE INNODB STATUS(节选):
```text
事务 1 的第二条 UPDATE: OK
事务 2 的第二条 UPDATE: OperationalError(1213, 'Deadlock found when trying to get lock; try restarting transaction')
提交后: ((1, 900), (2, 1100))
LATEST DETECTED DEADLOCK
*** (1) TRANSACTION:
UPDATE account SET balance = balance + 100 WHERE id = 2
*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 53 page no 4 n bits 72 index PRIMARY of table `demo`.`account` trx id 3933 lock_mode X locks rec but not gap
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 53 page no 4 n bits 72 index PRIMARY of table `demo`.`account` trx id 3933 lock_mode X locks rec but not gap waiting
*** (2) TRANSACTION:
UPDATE account SET balance = balance + 50 WHERE id = 1
*** WE ROLL BACK TRANSACTION (2)
```
## 参考资料
- Tanenbaum A S, Bos H. Modern Operating Systems [M]. Pearson, 2014.