mirror of
https://github.com/CyC2018/Interview-Notebook.git
synced 2026-10-30 07:33:45 +00:00
Update 计算机操作系统 - 死锁.md
调整银行家算法配图中的说明文字,统一列表标点,增加死锁示例的实测附录。
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 163 KiB |
+46
-14
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user