Skip to content

Commit a21f888

Browse files
author
王鹏
committed
feat(MEDIUM): add _1226_DiningPhilosophers
1 parent f3930af commit a21f888

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package pp.arithmetic.leetcode;
2+
3+
import java.util.concurrent.Semaphore;
4+
5+
/**
6+
* Created by wangpeng on 2020-07-17.
7+
* 1226. 哲学家进餐
8+
*
9+
* 5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
10+
*
11+
* 所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
12+
*
13+
* 假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
14+
*
15+
* 设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
16+
*
17+
*
18+
* 问题描述和图片来自维基百科 wikipedia.org
19+
* 图片地址:https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2019/10/23/an_illustration_of_the_dining_philosophers_problem.png
20+
*
21+
*
22+
* 哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):
23+
*
24+
* philosopher 哲学家的编号。
25+
* pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
26+
* eat 表示吃面。
27+
* putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
28+
* 由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
29+
* 给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。
30+
*
31+
*  
32+
*
33+
* 示例:
34+
*
35+
* 输入:n = 1
36+
* 输出:[[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]]
37+
* 解释:
38+
* n 表示每个哲学家需要进餐的次数。
39+
* 输出数组描述了叉子的控制和进餐的调用,它的格式如下:
40+
* output[i] = [a, b, c] (3个整数)
41+
* - a 哲学家编号。
42+
* - b 指定叉子:{1 : 左边, 2 : 右边}.
43+
* - c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。
44+
* 如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。
45+
*  
46+
*
47+
* 提示:
48+
*
49+
* 1 <= n <= 60
50+
*
51+
* 来源:力扣(LeetCode)
52+
* 链接:https://leetcode-cn.com/problems/the-dining-philosophers
53+
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
54+
*/
55+
public class _1226_DiningPhilosophers {
56+
57+
public static void main(String[] args) {
58+
DiningPhilosophers diningPhilosophers = new DiningPhilosophers();
59+
for (int i = 0; i < 5; i++) {
60+
int finalI = i;
61+
new Thread(){
62+
@Override
63+
public void run() {
64+
super.run();
65+
try {
66+
diningPhilosophers.wantsToEat(finalI, new Runnable() {
67+
@Override
68+
public void run() {
69+
System.out.println("["+finalI+",1,1]");
70+
}
71+
}, new Runnable() {
72+
@Override
73+
public void run() {
74+
System.out.println("["+finalI+",2,1]");
75+
}
76+
}, new Runnable() {
77+
@Override
78+
public void run() {
79+
try {
80+
Thread.sleep(1000);
81+
} catch (InterruptedException e) {
82+
e.printStackTrace();
83+
}
84+
System.out.println("["+finalI+",0,3]");
85+
}
86+
}, new Runnable() {
87+
@Override
88+
public void run() {
89+
System.out.println("["+finalI+",1,2]");
90+
}
91+
}, new Runnable() {
92+
@Override
93+
public void run() {
94+
System.out.println("["+finalI+",2,2]");
95+
}
96+
});
97+
} catch (InterruptedException e) {
98+
e.printStackTrace();
99+
}
100+
}
101+
}.start();
102+
}
103+
}
104+
105+
/**
106+
* 解题思路:
107+
* 资源:5个叉子(5个信号量),同时拿到两个叉子(1个互斥信号量,防止死锁),此题解并不是最优解,最优解应该能满足多个同时进餐
108+
*/
109+
static class DiningPhilosophers {
110+
//一个互斥信号量用于临界资源的互斥访问
111+
private Semaphore mutex;
112+
//5个同步信号量用于哲学家之间的同步访问
113+
private Semaphore[] sema;
114+
public DiningPhilosophers() {
115+
mutex = new Semaphore(1);
116+
sema = new Semaphore[] {
117+
new Semaphore(1),
118+
new Semaphore(1),
119+
new Semaphore(1),
120+
new Semaphore(1),
121+
new Semaphore(1)
122+
};
123+
}
124+
125+
// call the run() method of any runnable to execute its code
126+
public void wantsToEat(int philosopher,
127+
Runnable pickLeftFork,
128+
Runnable pickRightFork,
129+
Runnable eat,
130+
Runnable putLeftFork,
131+
Runnable putRightFork) throws InterruptedException {
132+
//一个哲学家如果要拿起叉子就同时拿两个,因此这里是一个原子操作,需要用mutex信号量包起来,表示互斥
133+
mutex.acquire();
134+
//尝试获取左手边的叉子
135+
sema[philosopher].acquire();
136+
//尝试获取右手边的叉子
137+
sema[(philosopher+1) % 5].acquire();
138+
139+
pickLeftFork.run();
140+
pickRightFork.run();
141+
//我认为这句话应该放在这里。
142+
// mutex.release();
143+
144+
//拿到叉子开始吃饭
145+
eat.run();
146+
147+
//吃完饭放下叉子
148+
putLeftFork.run();
149+
sema[philosopher].release();
150+
putRightFork.run();
151+
sema[(philosopher+1) % 5].release();
152+
mutex.release();
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)