Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5af517b

Browse files
committed
feat: add leetcode question #1229
1 parent 9b1910e commit 5af517b

File tree

8 files changed

+215
-3
lines changed

8 files changed

+215
-3
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.hi.dhl.algorithms.other.concurrency._1226;
2+
3+
import java.util.concurrent.atomic.AtomicInteger;
4+
5+
/**
6+
* <pre>
7+
* author: dhl
8+
* date : 2020年11月10日
9+
* desc :
10+
* </pre>
11+
*/
12+
class DiningPhilosophersCAS {
13+
private final AtomicInteger forks = new AtomicInteger(0);
14+
15+
public DiningPhilosophersCAS() {
16+
17+
}
18+
19+
// call the run() method of any runnable to execute its code
20+
public void wantsToEat(int philosopher,
21+
Runnable pickLeftFork,
22+
Runnable pickRightFork,
23+
Runnable eat,
24+
Runnable putLeftFork,
25+
Runnable putRightFork) throws InterruptedException {
26+
while (!forks.compareAndSet(0, 1)) Thread.sleep(1);
27+
pickLeftFork.run();
28+
pickRightFork.run();
29+
eat.run();
30+
putLeftFork.run();
31+
putRightFork.run();
32+
while (!forks.compareAndSet(1, 0)) Thread.sleep(1);
33+
}
34+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.hi.dhl.algorithms.other.concurrency._1226;
2+
3+
import java.util.concurrent.locks.ReentrantLock;
4+
5+
/**
6+
* <pre>
7+
* author: dhl
8+
* date : 2020年11月10日
9+
* desc :
10+
* </pre>
11+
*/
12+
13+
class DiningPhilosophersLock {
14+
private final ReentrantLock forkLock = new ReentrantLock();
15+
16+
public DiningPhilosophersLock() {
17+
18+
}
19+
20+
// call the run() method of any runnable to execute its code
21+
public void wantsToEat(int philosopher,
22+
Runnable pickLeftFork,
23+
Runnable pickRightFork,
24+
Runnable eat,
25+
Runnable putLeftFork,
26+
Runnable putRightFork) throws InterruptedException {
27+
forkLock.lock();
28+
try {
29+
pickLeftFork.run();
30+
pickRightFork.run();
31+
eat.run();
32+
putLeftFork.run();
33+
putRightFork.run();
34+
} finally {
35+
forkLock.unlock();
36+
}
37+
}
38+
}

‎README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
剑指 offer 及大厂面试题解:<a href ="https://offer.hi-dhl.com">在线阅读</a>,LeetCode 系列题解:<a href ="https://leetcode.hi-dhl.com">在线阅读</a>
77
</p>
88

9-
<p align="center"> 做题进度:AC 131 题,每道题目都会用 Java 和 kotlin 去实现</p>
9+
<p align="center"> 做题进度:AC 132 题,每道题目都会用 Java 和 kotlin 去实现</p>
1010

1111
<p align="center"> 每题都有解题思路、时间复杂度、空间复杂度、源代码</p>
1212

‎leetcode/common/index_header.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<p align="center"> 仓库 <b>每周</b> 持续更新,如果对你有帮助,请在右上角 star 一下</p>
44

5-
<p align="center"> LeetCode 的题解进度,目前已经 AC 了 131 题</p>
5+
<p align="center"> LeetCode 的题解进度,目前已经 AC 了 132 题</p>
66

77
<p align="center"> 每题都有解题思路、时间复杂度、空间复杂度、源代码</p>
88

‎offer/_sidebar.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* [多线程-打印零与奇偶数](/multi-thread/05-print-zero-even-odd.md)
1414
* [多线程-H2O 生成](/multi-thread/06-h20.md)
1515
* [多线程-交替打印字符串](/multi-thread/07-fizz-buzz.md)
16+
* [多线程-哲学家进餐](/multi-thread/08-dining-philosophers.md)
1617

1718
* 大厂面试题
1819

‎offer/common/index_header.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<p align="center"> 仓库 <b>每周</b> 持续更新,如果对你有帮助,请在右上角 star 一下</p>
44

5-
<p align="center"> LeetCode 的题解进度,目前已经 AC 了 131 题</p>
5+
<p align="center"> LeetCode 的题解进度,目前已经 AC 了 132 题</p>
66

77
<p align="center"> 每题都有解题思路、时间复杂度、空间复杂度、源代码 </p>
88

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
题目来源于 LeetCode 多线程 第 1226 号问题:哲学家进餐。题目难度为 Middle。
2+
3+
* [中文地址:https://leetcode-cn.com/problems/the-dining-philosophers](https://leetcode-cn.com/problems/the-dining-philosophers)
4+
5+
## 题目描述
6+
7+
5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
8+
9+
所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
10+
11+
假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
12+
13+
设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
14+
15+
![](http://img.hi-dhl.com/16049695340941.jpg)
16+
17+
哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):
18+
19+
* philosopher 哲学家的编号。
20+
* pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
21+
* eat 表示吃面。
22+
* putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
23+
* 由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
24+
25+
给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。
26+
27+
示例
28+
29+
```
30+
输入:n = 1
31+
输出:[[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]]
32+
解释:
33+
n 表示每个哲学家需要进餐的次数。
34+
输出数组描述了叉子的控制和进餐的调用,它的格式如下:
35+
output[i] = [a, b, c] (3个整数)
36+
- a 哲学家编号。
37+
- b 指定叉子:{1 : 左边, 2 : 右边}.
38+
- c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。
39+
如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。
40+
```
41+
42+
43+
## 思路:
44+
45+
根据题意:哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食,意味着哲学家如果想要进食需要同时拿起左右叉子。
46+
47+
我们可以构建一个临界区,每次允许一个哲学家进入这个临界区:拿起左右叉子 + 吃意面 + 放下左右叉子,流程走完之后退出临界区。
48+
49+
**这道题有多种实现方式:**
50+
51+
* 使用 Object 的 `wait/notify/notifyAll` 的消息通知机制
52+
* 使用 Lock 的 Condition 的 `await/signal/signalAll` 的消息通知机制
53+
* 通过 Semaphore(信号量) 实现,关于 AQS 和 Semaphore 原理以及使用注意事项,在之前的文章已经详细分析过了, 如果不了解可以前去查看 「 头条-线程交替打印 ABC 」
54+
* 通过 ReentrantLock 实现
55+
* 通过 CAS 实现
56+
57+
在之前的文章里面已经介绍过前三种,这里只演示
58+
59+
* 通过 ReentrantLock 实现
60+
* 通过 CAS 实现
61+
62+
### CAS 实现
63+
64+
CAS 是项乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
65+
66+
> 乐观锁是一种思想。CAS是这种思想的一种实现方式。
67+
68+
CAS 操作包含三个操作数 —— 内存中的值(V)、预期原值(A)和新值(B)。如果内存中的值与预期原值相匹配,会自动更新内存中的值为新值,否则一直在原地循环等待
69+
70+
71+
```
72+
class DiningPhilosophers {
73+
private final AtomicInteger forks = new AtomicInteger(0);
74+
75+
public DiningPhilosophers() {
76+
77+
}
78+
79+
// call the run() method of any runnable to execute its code
80+
public void wantsToEat(int philosopher,
81+
Runnable pickLeftFork,
82+
Runnable pickRightFork,
83+
Runnable eat,
84+
Runnable putLeftFork,
85+
Runnable putRightFork) throws InterruptedException {
86+
// 取内存的值 0 与 1 进行比较,如果不相等更新内存中的值为 1
87+
// 其他线程进来,因为资源没有释放,内存中的值 == 期待值 == 1 所以在这里一直循环等待释放资源
88+
while (!forks.compareAndSet(0, 1)) Thread.sleep(1);
89+
// 拿起左右叉子
90+
pickLeftFork.run();
91+
pickRightFork.run();
92+
// 吃意面
93+
eat.run();
94+
// 放下左右叉子
95+
putLeftFork.run();
96+
putRightFork.run();
97+
98+
// 流程走完之后通过 CAS 的方式修改内存中的值为 0,让其他线程可以进入临界区
99+
while (!forks.compareAndSet(1, 0)) Thread.sleep(1);
100+
}
101+
}
102+
```
103+
104+
105+
### ReentrantLock 实现
106+
107+
```
108+
class DiningPhilosophers {
109+
private final ReentrantLock forkLock = new ReentrantLock();
110+
111+
public DiningPhilosophers() {
112+
113+
}
114+
115+
// call the run() method of any runnable to execute its code
116+
public void wantsToEat(int philosopher,
117+
Runnable pickLeftFork,
118+
Runnable pickRightFork,
119+
Runnable eat,
120+
Runnable putLeftFork,
121+
Runnable putRightFork) throws InterruptedException {
122+
forkLock.lock(); // 同时只能允许一个哲学家进入临界区
123+
try {
124+
// 拿起左右叉子
125+
pickLeftFork.run();
126+
pickRightFork.run();
127+
// 吃意面
128+
eat.run();
129+
// 放下左右叉子
130+
putLeftFork.run();
131+
putRightFork.run();
132+
} finally {
133+
// 流程走完之后退出临界区
134+
forkLock.unlock();
135+
}
136+
}
137+
}
138+
```
139+
179 KB
Loading[フレーム]

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /