哲学家进餐避免死锁
进餐条件
5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/the-dining-philosophers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
死锁条件:5个哲学家同时拿起左边叉子
PS:死锁的 4个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
- 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
- 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
- 循环等待条件: 若干进程间形成首尾相接循环等待资源的关系。
作者:gfu
链接:https://leetcode-cn.com/problems/the-dining-philosophers/solution/1ge-semaphore-1ge-reentrantlockshu-zu-by-gfu/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解决方式:
1.同时拿起两边叉子,否则不拿
2.顺序拿起左边叉,同时进餐人数不超过四人
方案一:同时拿起两边叉子,否则不拿
代码如下:
void getFork(int i){
Semaphore left=semaphores[i];
Semaphore right=null;
if(i==4){
right=semaphores[0];
}else {
right=semaphores[i+1];
}
if(left.tryAcquire()){
System.out.println(i+"拿到叉子左:"+i);
if(right.tryAcquire()){
System.out.println(i+"拿到叉子右:"+String.valueOf(i+1));
execute(i);
right.release();
left.release();
System.out.println(i+"释放两个叉子");
}else {
System.out.println(i+"释放叉子左:"+i);
left.release();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}else {
}
int priority =Thread.currentThread().getPriority();
Thread.currentThread().setPriority(2);
Thread.yield();
System.out.println(priority);
}
方案二
顺序拿起左边叉,同时进餐人数不超过四人
1.设置拿叉子顺序,(除非拿起所有叉子,否则不释放锁,一直阻塞)
void getFork2(int i){
Semaphore left=semaphores[i];
Semaphore right=null;
if(i==4){
right=semaphores[0];
}else {
right=semaphores[i+1];
}
try {
left.acquire();
System.out.println(i+"拿到叉子左:"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
right.acquire();
System.out.println(i+"拿到叉子右:"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
execute(i);
left.release();
right.release();
}
2.利用线程池保证同时执行的线程不超过四个
ExecutorService executorService= Executors.newFixedThreadPool(5);
测试代码如下
package threads;
import org.junit.Test;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @description:哲学家进餐
* @author: HYW
* @create: 2020-01-03 15:08
*/
public class DiningPhilosophers {
int num=5;
private Semaphore[] semaphores=new Semaphore[4];
public DiningPhilosophers() {
for (int i = 0; i < num; i++) {
//每只叉子只有1个
semaphores[i] = new Semaphore(1);
}
}
void eat(int i){
i=i-1;
while(true){
//方案一
//getFork(i);
//方案二
getFork2(i);
Thread.yield();
/*try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
void getFork2(int i){
Semaphore left=semaphores[i];
Semaphore right=null;
if(i==4){
right=semaphores[0];
}else {
right=semaphores[i+1];
}
try {
left.acquire();
System.out.println(i+"拿到叉子左:"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
right.acquire();
System.out.println(i+"拿到叉子右:"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
execute(i);
left.release();
right.release();
}
void getFork(int i){
Semaphore left=semaphores[i];
Semaphore right=null;
if(i==4){
right=semaphores[0];
}else {
right=semaphores[i+1];
}
if(left.tryAcquire()){
System.out.println(i+"拿到叉子左:"+i);
if(right.tryAcquire()){
System.out.println(i+"拿到叉子右:"+String.valueOf(i+1));
execute(i);
right.release();
left.release();
System.out.println(i+"释放两个叉子");
}else {
System.out.println(i+"释放叉子左:"+i);
left.release();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}else {
}
int priority =Thread.currentThread().getPriority();
Thread.currentThread().setPriority(2);
Thread.yield();
System.out.println(priority);
}
void execute(int i){
System.out.println("哲学家"+ i+" 吃饭");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
DiningPhilosophers demo=new DiningPhilosophers();
ExecutorService executorService= Executors.newFixedThreadPool(4);
executorService.execute(new Runnable() {
@Override
public void run() {
demo.eat(1);
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
demo.eat(2);
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
demo.eat(3);
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
demo.eat(4);
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
demo.eat(5);
}
});
executorService.shutdown();
}
}
注意
线程池会把超过最大线程数的任务,放在队列种等待,所以第五位哲学家只有在前四位哲学家的线程吃完(停止)后,才可以吃饭。
改进方式,增加信号量,Semaphore eatnerNum= new Semaphore(4);
保证同时只有四人进餐