# 要求
哲学家从 0
到 4
按顺时针编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):
philosopher
哲学家的编号pickLeftFork
和pickRightFork
表示拿起左边或右边的叉子。eat
表示吃面putLeftFork
和putRightFork
表示放下左边或右边的叉子- 由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
给你 5
个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。
来源:https://leetcode-cn.com/problems/the-dining-philosophers
输入: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 号哲学家拿起了右边的叉子。 |
# 解法
newCondition () 方法
关键字 synchronized
与 wait()/notify()
这两个方法一起使用可以 实现等待/通知模式
, Lock
锁的 newContition()
方法返回 Condition
对象, Condition
类也可以 实现等待/通知模式
。用 notify()
通知时, JVM
会随机唤醒某个等待的线程, 使用 Condition
类可以进行选择性通知。Condition
比较常用的两个方法:
await()
会使当前线程等待,同时会释放锁,当其他线程调用signal()
时,线程会重新获得锁并继续执行。signal()
用于唤醒一个等待的线程。
注意:在调用 Condition
的 await()/signal()
方法前,也需要线程持有相关的 Lock
锁,调用 await()
后线程会释放这个锁,在 singal()
调用后会从当前 Condition
对象的等待队列中,唤醒 一个线程,唤醒的线程尝试获得锁, 一旦获得锁成功就继续执行。
ReentrantLockReentrantLock
的实现是基于其内部类 FairSync
(公平锁) 和 NonFairSync
(非公平锁) 实现的。 其可重入性是基于 Thread.currentThread()
实现的:如果当前线程已经获得了执行序列中的锁,那执行序列之后的所有方法都可以获得这个锁。
class DiningPhilosophers { | |
// 定义 lock | |
public ReentrantLock lock = new ReentrantLock(); | |
// 定义 Condition 对象 | |
public Condition[] conditions = new Condition[5]; | |
// 叉子是否被占用 | |
public boolean[] forks = new boolean[5]; | |
public DiningPhilosophers() { | |
// 创建 conditions 对象 | |
for(int i = 0 ; i < 5 ; i++){ | |
conditions[i]=lock.newCondition(); | |
} | |
} | |
// 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 { | |
lock.lock(); | |
try{ | |
// 定义哲学家左右叉子编号 | |
int leftFork=philosopher; | |
int rightFork=(philosopher+1)%5; | |
// 左右任意一个叉子被占用,线程等待 | |
if(forks[leftFork]||forks[rightFork]){ | |
conditions[philosopher].await(); | |
} | |
// 占用叉子 | |
forks[rightFork]=true; | |
forks[leftFork]=true; | |
// 吃面 | |
pickLeftFork.run(); | |
pickRightFork.run(); | |
eat.run(); | |
// 放下左边的叉子 | |
putLeftFork.run(); | |
forks[leftFork]=false; | |
// 唤醒左边的哲学家 | |
conditions[leftFork].signal(); | |
putRightFork.run(); | |
// 唤醒右边的哲学家 | |
forks[rightFork]=false; | |
conditions[rightFork].signal(); | |
}finally{ | |
lock.unlock(); | |
} | |
} | |
} |