哲学家吃饭问题是计算机科学中经典的多线程同步问题之一。问题描述如下:
有五个哲学家围坐在圆桌上,进行思考和进食。哲学家们只做两件事:思考和吃饭。他们每人有一只碗,和一只筷子。每个哲学家面前都有一碗饭。他们思考时,不吃饭,吃饭时,不思考。
这个问题的关键在于,每个哲学家都必须等待左右两边的哲学家都吃完饭才能开始吃饭。也就是说,哲学家们必须按照一个方向开始吃饭。当一个哲学家吃饭的时候,他必须等待他左右两边的哲学家都吃完。当一个哲学家思考的时候,他不会阻止其他哲学家吃饭。
这个问题可以用Java的synchronized关键字来解决。我们可以创建一个类来表示一个哲学家,每个哲学家只有同时拿到左右手的筷子才能吃饭,其他人只能等待旁边的哲学家吃完饭才能吃饭。
一般情况下,可能出现的是这样的情况,就是每个哲学家同时拿到左手的筷子,那么他们都会在等待另一个人放下筷子,他才能拿到右手的筷子,才能去吃饭,但是如果都同时拿起左手的筷子,那么就都没有右手的筷子,这里模拟一下死锁的情况:
1 | // 创建一个独立的类,表示筷子 |
1 | // 创建一个类,表示这种死锁情况 |
这里没有定义eat()方法表示吃饭,而是直接在run方法里面执行完全部的步骤。每个哲学家都创建了两根筷子,表示只有拿到两根筷子才能开始吃饭,index表示哲学家的编号。run方法里面的执行方法模拟的就是,每个哲学家先拿到左手的筷子,都在等待右手的筷子。但是每个哲学家都已经同时拿起了左手的筷子,那么右手的筷子也就没有了,那么都在等待右手的筷子,导致谁也吃不了饭。(当前人的右手筷子是一下个人左手的筷子)其中,timeSleep方法只是处理Thread.sleep()异常的封装,无特殊处理。
上面的情况就是所有人都在等待拿右手的筷子,但是谁都拿不到,造成谁也吃不了的局面。要解决这个问题,可以给出一个方案,就是偶数的哲学家先拿到右手的筷子,再拿左手的筷子先吃,吃饭就放下两根筷子;奇数的哲学家拿到先拿左手的筷子,再拿右手的筷子再吃饭,这样问题就解决了。
下面是解决这一问题的代码:
1 | public class P02_DeadLockOpen { |
执行结果如下:
需要注意的是,打印的顺序是线程控制的,不一定与执行的顺序一致。(参考线程的生命周期)
这样就解决了哲学家吃饭问题,这里考虑执行优先级和时间都是不对的。如果给每个哲学家一个固定的优先级或时间片,那么会出现某些哲学家一致得不到执行的情况,这会导致其他哲学家也会跟着挨饿,即死锁问题。
本文链接: https://longzas.github.io/2023/10/02/%E5%93%B2%E5%AD%A6%E5%AE%B6%E5%90%83%E9%A5%AD%E9%97%AE%E9%A2%98/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!