什么是哲学家就餐问题
有五个哲学家在一张桌上,他们交替思考和吃饭。每个人只能拿自己左右手边的叉子,当他们拿到两只叉子的时候才能吃饭,吃完饭就放下叉子开始思考,每个哲学家不能同时拿起两只叉子。用程序实现这个过程。
问题分析
根据题意,每个哲学家交替吃饭和思考,吃饭的条件是拿到两只叉子,那么如果五个哲学家都同时拿了左手边的叉子,那么永远都不可能有人能吃到饭,只能饿死。为了避免这种情况的发生,我们一次只能允许四个哲学家去拿叉子,让一个人暂时思考,等待别人吃完饭,他再去拿叉子。
在java中,我们可以利用信号量Semaphore
来实现,代码如下:
代码
import java.util.concurrent.Semaphore;
public class PhilosopherTest {
//一次只允许四个人抢叉子
static final Semaphore count = new Semaphore(4);
//五只叉子
static final Semaphore[] mutex = {new Semaphore(1),
new Semaphore(1),
new Semaphore(1),
new Semaphore(1),
new Semaphore(1)};
static class Philosopher extends Thread {
Philosopher(String name) {
super.setName(name);
}
@Override
public void run() {
do {
try {
//只有四个人有抢叉子的资格
count.acquire();
Integer i = Integer.parseInt(super.getName());
//规定都先拿左手边的叉子,于是四个人左手都有叉子
mutex[i].acquire();
//大家开始抢右边的叉子
mutex[(i + 1) % 5].acquire();
//谁先抢到谁第一个开吃
System.out.println("哲学家" + i + "号吃饭!");
//吃完放下左手的叉子,对于左边人来说,就是他的右叉子,直接开吃
mutex[i].release();
//再放下右手的叉子
mutex[(i + 1) % 5].release();
//吃完了,开始思考,由于放下了右手的叉子,相当于给一个叉子没有的哲学家一个左叉子
count.release();
//模拟延迟
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("异常");
}
} while (true);
}
}
public static void main(String[] args) {
//五个哲学家
Philosopher p0 = new Philosopher("0");
Philosopher p1 = new Philosopher("1");
Philosopher p2 = new Philosopher("2");
Philosopher p3 = new Philosopher("3");
Philosopher p4 = new Philosopher("4");
p0.start();
p1.start();
p2.start();
p3.start();
p4.start();
}
}
对于代码的一些解释
1、static final Semaphore count = new Semaphore(4);
Semaphore通过acquire()
和count.release()
方法限制了并发中线程的访问数量,这里5个线程一次只允许四个参与竞争,这样就避免了死锁。
2、static final Semaphore[] mutex = {new Semaphore(1), new Semaphore(1), new Semaphore(1), new Semaphore(1), new Semaphore(1)};
哲学家们都去争夺mutex数组中的叉子,通过信号量数组,保证一只叉子只能在一个哲学家手中。
3、mutex[(i + 1) % 5].acquire();
当i = 0时,(i + 1) % 5 = 1;当i = 1时,(i + 1) % 5 = 2;当i = 4时,(i + 1) % 5 = 1,通过这种方式,使叉子的摆放形成一个圈。