【案例8-5】 小朋友就餐问题
【案例介绍】
1.任务描述
一圆桌前坐着5位小朋友,两个人中间有一只筷子,桌子中央有面条。小朋友边吃边玩,当饿了的时候拿起左右两只筷子吃饭,必须拿到两只筷子才能吃饭。但是,小朋友在吃饭过程中,可能会发生5个小朋友都拿起自己右手边的筷子,这样每个小朋友都因缺少左手边的筷子而没有办法吃饭。本案例要求编写一个程序解决小朋友就餐问题,使每个小朋友都能成功就餐。
2.运行结果
运行结果如图8-1所示。

图8-1 运行结果
【案例思路】
- 查看运行结果分析后,每个小朋友相当于一个线程,所以先创建一个Philosopher()方法作为小朋友。
- 查看运行结果分析后,创建eating()方法作为小朋友吃饭时的线程,创建thinking()方法作为小朋友玩耍时的线程。
- 查看运行结果分析后,需要在获取筷子的方法Fork中先定义一个boolean类型的数组,代表5根筷子的使用情况;再使用synchronized线程锁来控制只有左右手的筷子都未被使用时,才允许获取筷子,且必须同时获取左右手筷子。
- 查看运行结果分析后,需要在释放左右手筷子的方法putFork中使用synchronized线程锁来释放筷子。
- 最后在Test测试类中调用5次以上方法,代表5位小朋友。
【案例代码】
小朋友就餐问题的程序代码实现如文件8-1所示。
文件8-1 Philosopher.java
- package chapter0805;
- /*每个小朋友相当于一个线程*/
- public class Philosopher extends Thread{
- private String name;
- private Fork fork;
- public Philosopher(String name,Fork fork){
- super(name);
- this.name=name;
- this.fork=fork;
- }
- public void run(){
- while(true){
- thinking();
- fork.takeFork();
- eating();
- fork.putFork();
- }
- }
- public void eating(){
- System.out.println("小朋友"+name+"在吃饭");
- try {
- sleep(1000);//模拟吃饭,占用一段时间资源
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- public void thinking(){
- System.out.println("小朋友"+name+"在玩游戏");
- try {
- sleep(1000);//模拟思考
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- class Fork{
- /*5只筷子,初始为都未被用*/
- private boolean[] used={false,false,false,false,false};
- /*只有当左右手的筷子都未被使用时,才允许获取筷子,且必须同时获取左右手筷子*/
- public synchronized void takeFork(){
- String name = Thread.currentThread().getName();
- int i = Integer.parseInt(name);
- while(used[i]||used[(i+1)%5]){
- try {
- wait();//如果左右手有一只正被使用,等待
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- used[i ]= true;
- used[(i+1)%5]=true;
- }
- /*必须同时释放左右手的筷子*/
- public synchronized void putFork(){
- String name = Thread.currentThread().getName();
- int i = Integer.parseInt(name);
- used[i]= false;
- used[(i+1)%5]=false;
- notifyAll();//唤醒其他线程
- }
- }
文件8-1中第3~18行代码封装一个小朋友的方法,第19~27行代码是封装了小朋友吃饭时的方法,第28~37行代码封装了小朋友玩耍时的方法,第38~64行代码封装了筷子使用情况的方法。
测试类的代码如文件8-2所示,调用5次Fork代表5个小朋友。
文件8-2 Test.java
- package chapter0805;
- public class Test {
- public static void main(String []args){
- Fork fork = new Fork();
- new Philosopher("0",fork).start();
- new Philosopher("1",fork).start();
- new Philosopher("2",fork).start();
- new Philosopher("3",fork).start();
- new Philosopher("4",fork).start();
- }
- }