标签:分配 ati 线程 被占用 output limit 过程 实现 请求
如果一个进程集合中的每个进程都在等待只能只能有该集合中的其他一个进程才能引发的事件, 这种情况就是死锁。
简单举例
资源 A 与 资源 B 都是不可剥夺资源
进程 C 已经申请到资源A, 进程D已经申请到了资源B
进程 C 此时申请资源B, 而进程D恰好申请了资源A
由于资源已被占用, 进程A和进程B都无法执行下一步操作, 就造成了死锁。
预防死锁
避免死锁
死锁 检测 和 解除
资源分配策略 | 各种可能模式 | 主要优点 | 主要缺点 | |
---|---|---|---|---|
死锁预防 | 保守, 宁可资源限制 | 一次请求所有的资源, 资源剥夺, 资源按序分配 | 适用于做突发式处理的进程, 不必进行剥夺。 | 效率低, 进程初始化时间延长; 剥夺次数过多; 不便灵活申请新资源。 |
死锁避免 | “预防” 和 “检测” 的折中 | 寻找可能的安全允许顺序 | 不必进行剥夺 | 必须知道将来的资源需求; 进程不能被长时间阻塞。 |
死锁检测 | 宽松, 只要允许就分配资源 | 定期检查死锁是否已经发生 | 不延长进程初始化时间, 允许对死锁进行现场处理。 | 通过剥夺解除死锁造成的损失。 |
银行家算法:
解除死锁的方法
Leetcode 1226题, 哲学家进餐
5个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):
philosopher
哲学家的编号。pickLeftFork
和 pickRightFork
表示拿起左边或右边的叉子。eat
表示吃面。putLeftFork
和 pickRightFork
表示放下左边或右边的叉子。给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。
在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。
示例:
输入:n = 1
输出:[[4,2,1],[4,1,1],[0,1,1],[2,2,1],[2,1,1],[2,0,3],[2,1,2],[2,2,2],[4,0,3],[4,1,2],[0,2,1],[4,2,2],[3,2,1],[3,1,1],[0,0,3],[0,1,2],[0,2,2],[1,2,1],[1,1,1],[3,0,3],[3,1,2],[3,2,2],[1,0,3],[1,1,2],[1,2,2]]
解释:
n 表示每个哲学家需要进餐的次数。
输出数组描述了叉子的控制和进餐的调用,它的格式如下:
output[i] = [a, b, c] (3个整数)
- a 哲学家编号。
- b 指定叉子:{1 : 左边, 2 : 右边}.
- c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。
如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。
解法一
package com.ronnie.leetcode;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;
public class DiningPhilosophers {
// 1 fork => 1 ReetrantLock
private ReentrantLock[] lockList = {
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()
};
// restriction: Only 4 Philosophers can get at least one fork
private Semaphore eatLimit = new Semaphore(4);
public DiningPhilosophers() {
}
// call the run() method of any runnable to execute its code
public void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
int leftFork = (philosopher+1)%5;
int rightFork = philosopher;
eatLimit.acquire();
lockList[leftFork].lock();
lockList[rightFork].lock();
pickLeftFork.run();
pickRightFork.run();
eat.run();
putLeftFork.run();
putRightFork.run();
lockList[leftFork].unlock();
lockList[rightFork].unlock();
eatLimit.release();
}
}
解法二
package com.ronnie.leetcode;
import java.util.concurrent.locks.ReentrantLock;
public class DiningPhilosophers2 {
// 1 fork => 1 ReentrantLock
private ReentrantLock[] lockList = {
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()
};
// 思路: 设置一个临界区, 进入临界区后, 只有当哲学家获取到左右两把叉子并执行代码后, 才退出临界区
private ReentrantLock pickBothForks = new ReentrantLock();
public DiningPhilosophers2(){
}
// call the run() method of any runnable to execute its code
public void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
int leftFork = (philosopher + 1) % 5;
int rightFork = philosopher;
// 进入临界区
lockList[leftFork].lock();
lockList[rightFork].lock();
pickLeftFork.run();
pickRightFork.run();
// 退出临界区
pickBothForks.unlock();
putLeftFork.run();
pickRightFork.run();
lockList[leftFork].unlock();
lockList[rightFork].unlock();
}
}
解法三
package com.ronnie.leetcode;
import java.util.concurrent.locks.ReentrantLock;
/**
* 只有当5个哲学家都左手持其左边的叉子 或 都右手持有期右边的叉子时, 才会发送死锁。
*/
public class DiningPhilosophers3 {
// 1 fork => 1 ReetrantLock
private ReentrantLock[] lockList = {
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()
};
public DiningPhilosophers3() {
}
// call the run() method of any runnable to execute its code
public void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
int leftFork = (philosopher + 1) % 5;
int rightFork = philosopher;
// 让编号为偶数的哲学家优先拿左边, 再拿右边
if (philosopher % 2 == 0){
lockList[leftFork].lock();
lockList[rightFork].lock();
} else {
// 编号为奇数的旧先拿右边再拿左边, 这样就避免了死锁
lockList[rightFork].lock();
lockList[leftFork].lock();
}
pickLeftFork.run();
pickRightFork.run();
eat.run();
putLeftFork.run();
putRightFork.run();
lockList[leftFork].unlock();
lockList[rightFork].unlock();
}
}
标签:分配 ati 线程 被占用 output limit 过程 实现 请求
原文地址:https://www.cnblogs.com/ronnieyuan/p/11828316.html