问题描述
- 哲学家进餐问题:5个哲学家共用一张圆桌,分别坐在周围的5张椅子上,在圆桌上有5个碗和5只筷子(注意是5只筷子,不是5双),碗和筷子交替排列。他们的生活方式是交替地进行思考(thinking)和进餐(eating)。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的两只筷子,规定他必须先取左边的筷子,再取右边的筷子。只有在他拿到两只筷子时才能进餐。进餐完毕,放下筷子继续进行思考。假如5位哲学家同时饥饿,各自拿起左边的筷子时,再去拿各自右边的筷子,因为无筷子可拿而陷入无期限等待(死锁)。一种解决的办法是:至多只允许有4位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够拿到两只筷子,从而进餐。进餐完毕释放他用过的两只筷子,从而使更多的哲学家能够进餐。使用Java的多线程同步技术,实现上述解决方案。
主方法
package cn.learn.thread.ThreadState.practice;
//测试
public class Test {
public static void main(String[] args) {
//五根筷子对象
Chopsticks[] chopsticks = new Chopsticks[5];
for (int i = 0; i <5 ; i++) {
chopsticks[i] = new Chopsticks("---筷子"+i);
}
//创建五位哲学家进程,并传递其左右两边筷子
for (int i = 0; i < 5; i++) {
Philosopher philosopher = new Philosopher("哲学家"+(i + 1)+": ",chopsticks[(i+1)%5],chopsticks[i]);
philosopher.start();
}
}
}
筷子类
package cn.learn.thread.ThreadState.practice;
/*
共享资源:筷子
*/
public class Chopsticks {
private String chopsticksName;
public Chopsticks(String chopsticksName) {
this.chopsticksName = chopsticksName;
}
public String getChopsticksName() {
return chopsticksName;
}
public void setChopsticksName(String chopsticksName) {
this.chopsticksName = chopsticksName;
}
}
哲学家进程
package cn.learn.thread.ThreadState.practice;
import java.util.Random;
public class Philosopher extends Thread {
//左筷子
private Chopsticks leftChopsticks;
//右筷子
private Chopsticks rightChopsticks;
//静态引用类型保证同步时为同一把锁
private static Object obj = new Object();
private static int flag = 5;
public Philosopher(String name, Chopsticks leftChopsticks, Chopsticks rightChopsticks) {
//调用父方法给进程取名字
super(name);
this.leftChopsticks = leftChopsticks;
this.rightChopsticks = rightChopsticks;
}
@Override
public void run() {
while (true) {
thinking();
takeChopsticks();
}
}
//思考
private void thinking() {
//获取当前进程名称
String threadName = Thread.currentThread().getName();
Random random = new Random();
//睡眠,不释放锁
try {
sleep(random.nextInt(10000)+1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName + "思考中。。。");
}
//拿筷子
public void takeChopsticks() {
//让前四个哲学家先拿筷子
synchronized (obj) {
//如果只剩一个哲学家让他等待
if (flag == 1) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//思考人数--
flag--;
}
//进程进来先给左筷子请求进入并上锁,再请求右筷子
synchronized (leftChopsticks) {
//获取当前进程名称
String threadName = Thread.currentThread().getName();
//获取当前进程筷子
String chopsticksName = leftChopsticks.getChopsticksName();
//哲学家拿左筷子
System.out.println(threadName + "拿起一根左筷子" + chopsticksName);
synchronized (rightChopsticks) {
//获取当前进程筷子
chopsticksName = rightChopsticks.getChopsticksName();
System.out.println(threadName + "拿起一根右筷子" + chopsticksName + "开始吃饭!");
}
//方法结束释放锁对象筷子
}
//唤醒没有拿到筷子的哲学家
synchronized (obj) {
obj.notify();
flag++;
}
}
}
代码总结
- 五个哲学家在争抢筷子时,由系统调度,从而可能发生死锁,通过设置flag来解决使得有一名哲学家不能进餐,当有一个人进餐完成后,flag++,思考的人员增加;通过随机设置思考睡眠时间,可以达到五个哲学家可以随机有两个人同时吃饭。