场景
有五位沉默的哲学家围坐在一张圆桌旁,他们一生都在吃东西和思考。
有五只筷子供他们使用,哲学家需要双手拿到一双筷子之后才能吃饭;吃完后会将筷子放下继续思考。
那么现在有一个问题,我们需要想出一种方案,如何保证哲学家们可以交替吃饭和思考,而不会被饿死。
上面这个问题是由Dijkstra提出的一个经典的线程同步问题。
解决方案
我们在开始想如何解决问题之前,可以先将这个场景通过代码还原,在程序中进行建模。
每一只筷子可以看做是一个资源数据,都可以被它两边的哲学家尝试去获取,并且同一时间只能由其中一人持有,这可以通过我们JUC包中的信号量Semaphore来表示。
然后,每个哲学家可以看做是一个线程,每个线程中的run方法内容都是先进行思考,然后试图获取左右两边的筷子吃饭,吃完饭后继续思考。
通过上面的分析,我们的代码实现如下:
/**
* @author 小黑说Java
* @ClassName DiningPhilosophers
* @Description 哲学家就餐问题
* @date 2022/2/6
**/
@Slf4j
public class DiningPhilosophers implements Runnable {
private final int id;
public DiningPhilosophers(int id) {
this.id = id;
}
private static final Random random = new Random(System.currentTimeMillis());
private static final Semaphore[] forks = new Semaphore[5];
// 初始化信号量,每个信号量为1,代表1只筷子
static {
forks[0] = new Semaphore(1);
forks[1] = new Semaphore(1);
forks[2] = new Semaphore(1);
forks[3] = new Semaphore(1);
forks[4] = new Semaphore(1);
}
@Override
public void run() {
try {
while (true) {
think();
eat(id);
}
} catch (InterruptedException e) {
log.error("异常中断", e);
}
}
/**
* 哲学家思考随机时间
*/
private void think() throws InterruptedExc