前言
一、任务介绍
1.任务描述
一圆桌前坐着5位小朋友,两个人中间有一只筷子,桌子中央有面条。小朋友边玩边吃,玩一会,觉得饿了的时候拿起左右两只筷子吃一会饭,必须拿到两只筷子才能吃饭,然后后接着玩,饿了再吃点,如此反复。但是,小朋友在吃饭过程中,可能会发生5个小朋友都拿起自己右手边的筷子,则所有小朋友都处于等待状态,将产生死锁现象,这样每个小朋友都因缺少左手边的筷子而没有办法吃饭。本次实验任务要求编写一个程序解决小朋友就餐问题,使每个小朋友都能成功就餐,实现运行结果的效果。
2.运行结果
3.任务目标
学会分析“小朋友就餐问题”任务实现的逻辑思路。
能够根据分析参考完成“小朋友就餐问题”程序的源代码的完善、编译以及运行。
掌握生成多个线程的方法和运行机制,了解多线程共享资源问题的多途径和区别。
通过“小朋友就餐问题”程序了解多线程死锁问题,并初步掌握解决多线程死锁问题。
在这里插入图片描述
4.实现思路
根据运行结果分析:
(1) 每个小朋友相当于一个线程,所以先创建一个Child线程子类,在线程的run( )方法中小朋友先玩一会,然后拿起筷子吃会饭,吃会饭后又放下筷子接着玩,如此反复。
(2) 在Child类中可创建eating( )方法模拟小朋友吃饭,在eating( )方法输出小朋友正在吃饭的信息,并结合sleep( )模拟吃饭占用会时间,创建playing( )方法模拟小朋友玩耍,在playing()方法中输出小朋友正在玩耍的信息,并结合sleep( )模拟玩耍占用会时间。
(3) 所有小朋友线程都共享圆桌上的筷子(共5根筷子),可定义一个chopsticks类,在该类中可定义一个包含5个boolean类型变量的数组,用来代表5根筷子的使用情况,数组中的5元素值可均初始化为false,表示5根筷子在初始状态未被占用,在小朋友调用chopsticks的takeChopsticks( )和putChopsticks()时筷子的状态会相应更新。
(4) chopsticks类中可定义synchronized方法takeChopsticks( )用来刻画筷子被小朋友拿起,还需定义synchronized方法putChopsticks用来刻画筷子被小朋友放下,本程序解决死锁的方法是小朋友想吃饭时要想拿就拿一双筷子,即左右筷子如未被占用,小朋友就将左右筷子都拿起,吃完饭后小朋友会将左右筷子都放下。为了实现以上效果在takeChopsticks( )中需判定当前试图拿筷子的小朋友是否左右筷子都未被占用,否则当前小朋友等待,直到满足条件同时拿到左右的筷子,即更新左右的筷子的状态为true。putChopsticks( )方法中小朋友左右的筷子会都被放下,即更新当前小朋友左右的筷子状态为false.
(5) Test测试类中需生成5个小朋友线程,调用有参(两个参数)的构造方法,一个参数用于将共享的筷子对象传入,一个参数用于传入线程的名称。为了根据当前小朋友线程的名字获得小朋友左右筷子的在筷子数组中的序号,以便获得和更新筷子的状态。小朋友的名字即线程名可采用数字串的形式,如“0”代表小朋友1线程的名字,“1”代表小朋友2线程的名字。这样就可获得名字为“0”的小朋友线程左右的筷子序号为0和1,名字“1”的小朋友线程左右的筷子序号为1和2,以此类推。(注意名字为“4”的小朋友左右的筷子序号为4和0,可统一通过求模运算建立对应关系,在运算前先完成整型数值字符串到相对应整数值的转换)
二、程序实现
这次的实验是填空形式布置的,这里面我已经填了
1.代码
//每个小朋友相当于一个线程
public class Child extends Thread{
private String name;
private Chopsticks chopsticks;
public Child(String name,Chopsticks chopsticks){
super(name);
this.name=name;
this.chopsticks=chopsticks;
}
public void run(){
while(true){
playing();
chopsticks.takeChopsticks();
eating();
chopsticks.putChopsticks();
}
}
public void eating(){
System.out.println("小朋友"+name+"在吃饭");
try {
sleep(1000);//模拟吃饭,占用一段时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void playing(){
System.out.println("小朋友"+name+"在玩游戏");
try {
sleep(1000);//模拟玩耍,占用一段时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Chopsticks{
//5只筷子,初始为都未被用
private boolean[] used={false,false,false,false,false};
//只有当左右的筷子都未被使用时,才允许获取筷子,且必须同时获取左右筷子
public synchronized void takeChopsticks(){
//请完善程序,获得当前小朋友线程的名字
String name =Thread.currentThread().getName();
//请完善程序,获得当前小朋友线程的名字所对应的整数值
int i =Integer.parseInt(name);
//请完善程序中的条件,判定当前小朋友左右的筷子是否都未被占用,否则等待
while(used[i]||used[(i+1)%5]){
try {
wait();//如果左右手有一只正被使用,等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
used[i ]= true;
used[(i+1)%5]=true;
}
/*必须同时释放左右手的筷子*/
public synchronized void putChopsticks(){
String name = Thread.currentThread().getName();
int i = Integer.parseInt(name);
//请完善程序,将当前小朋友左右的筷子状态更新为未被占用
used[i]=false;
used[(i+1)%5]=false;
//请完善程序,唤醒所有正在等待的小朋友线程
notifyAll();
}
}
class Test {
public static void main(String []args){
Chopsticks chopsticks = new Chopsticks();
new Child("0",chopsticks).start();
//请完善程序,再依次生成四个小朋友线程并启动
new Child("1",chopsticks).start();
new Child("2",chopsticks).start();
new Child("3",chopsticks).start();
new Child("4",chopsticks).start();
}
}
2.实验结果(包括输入数据和输出结果)
三、总结
多线程死锁问题