利用JAVA多线程与Swing编程制作GUI演示哲学家问题解决
感谢出现在GUI中的我以及我的四位室友并且感谢他们为我的程序设计提供一部分灵感让该演示显得不那么单调。
问题描述:
由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题。该问题是描述有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐完毕,放下筷子继续思考。
为了解决互斥问题,必须将筷子资源作为同步资源。在同一时刻,对于一个筷子对象,只能由一个哲学家(线程)对其进行访问。由于Java程序语言提供了同步机制,可以轻松解决线程互斥问题。
算法设计思路:
定义哲学家类与筷子类,筷子类包含放下筷子与拿起筷子同步方法分别属于v和p操作,共同竞争资源信号量--五双筷子。哲学家继承Thread类实现多线程,拥有think和eat方法以及重写的run方法。为避免哲学家竞争同一根筷子以及防止所有哲学家都只拿了一根筷子形成死锁的情况的发生,我采用了饥饿哲学家同时拿起自己左右筷子的方法。当然也可以采用规定奇数编号哲学家先取其左侧筷子,成功后再取其右侧筷子;而偶数编号哲学家则相反,先取其右侧筷子,成功后再取其左侧筷子的算法。这样可以保证一个哲学家能取到两只筷子。
算法伪代码如下:
semaphore chopstick[5]={1,1,1,1,1};
do{
...
//think
...
Swait(chopstick[(i+1)%5],chopstick[i]);
...
//eat
...
Signial(chopstick[(i+1)%5],chopstick[i]);
}while[TRUE];
1. 定义筷子类。筷子属于同步资源,被持有者持有,同时被索取者竞争。因此,取筷子和放筷子的操作必须作好同步。
public class chopsticks{
boolean[] chopsticks = {true,true,true,true,true};//创建信号量数组,模拟五根筷子,可用筷子初始化为true
public synchronized void takechopsticks(哲学家 a){
while(!chopsticks[a.num]||!chopsticks[(a.num+1)%5])//只有左右筷子均可用时,才允许就餐,避免进程死锁
{
try{
a.setface(2);//左右筷子均不可用时,哲学家进入饥饿状态
a.a=null;
a.d=a.name+"正在挨饿";
wait();//有筷子正在被使用,进程等待
}catch (Exception e){
}
}
//拿起筷子
chopsticks[a.num]=false;
chopsticks[(a.num+1)%5]=false;
}
public synchronized void putchosticks(int num){
//放下筷子
chopsticks[num]=true;
chopsticks[(num+1)%5]=true;
// 唤醒所有等待进程,使饥饿状态下的哲学家可以就餐
notifyAll();
}
}
2.定义哲学家类。对每位哲学家进行编号,一个哲学家对应一个人名,并编写其对应的思考方式,以及吃饭时随机选中一种食物,哲学家参与资源的竞争,因此申明为一个线程,重写其run()方法,
不断切换思考和吃饭两个动作(思考和吃饭设置一个随机时间)。
import javax.swing.*;
import java.net.URL;
import java.util.Random;
public class 哲学家 extends Thread {
int num;
chopsticks c;
String name;
String a=null,d=null;
public static final int first = 0;
public static final int think = 1;
public static final int hungry = 2;
public static final int eat = 3;
private ImageIcon currentFace,First,Think,Hungry,Eat;
public 哲学家(int num, String name, chopsticks c) {
this.num = num;
this.name = name;
this.c = c;
URL f=哲学家.class.getResource("start.jpg");
URL t=哲学家.class.getResource("think.jpg");
URL h=哲学家.class.getResource("hunger.jpg");
URL e=哲学家.class.getResource("eat.jpg");
First=new ImageIcon(f);
Think=new ImageIcon(t);
Hungry=new ImageIcon(h);
Eat=new ImageIcon(e);
setface(0);
}
public void think() {
if (num == 0) {
System.out.println("伟大的zzu马顿--" + name + "正在努力思考苹果为什么会掉在地上");
a="伟大的zzu马顿--" + name + "正在努力思考苹果为什么会掉在地上";
}
if (num == 1) {
System.out.println("伟大的zzu鸭梨是多德--" + name + "正在努力思考形而上学哲学观");
a="伟大的zzu鸭梨是多德--" + name + "正在努力思考形而上学哲学观";
}
if (num == 2) {
System.out.println("伟大的zzu柏拉兔--" + name + "正在努力创作理想国");
a="伟大的zzu柏拉兔--" + name + "正在努力创作理想国";
}
if (num == 3) {
System.out.println("伟大的zzu孔纸--" + name + "正在努力创建儒家学派");
a="伟大的zzu孔纸--" + name + "正在努力创建儒家学派";
}
if (num == 4) {
System.out.println("伟大的zzu王阳暗--" + name + "正在努力研究知行合一");
a="伟大的zzu王阳暗--" + name + "正在努力研究知行合一";
}
setface(1);
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void eat() {
Random b = new Random();
String[] a = {"正在北餐二楼吃担担面", "正在北餐一楼吃小火锅", "正在南餐吃大妈牌盖浇饭", "正在宿舍可怜兮兮地吃康师傅红烧牛肉面", "正在北二吃麻辣香锅"};
System.out.println(name + a[b.nextInt(4)]);
d=name +a[b.nextInt(4)];
setface(3);
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
while (true) {
think();
c.takechopsticks(哲学家.this);//P操作,拿起左右筷子,可用资源信号量-2
a=null;
eat();
c.putchosticks(num);//哲学家吃完饭,执行V操作,放下左右筷子,释放两个资源信号量
d=null;
}
}
public void setface(int a){
switch (a) {
case 哲学家.first:
currentFace = First;
break;
case 哲学家.think:
currentFace = Think;
break;
case 哲学家.hungry:
currentFace = Hungry;
break;
case 哲学家.eat:
currentFace = Eat;
break;
}
}
public ImageIcon getface(){
return currentFace;
}
}
3.编写UI类,此类对主干代码影响不大,仅仅是为了让演示更形象风趣,采用手段也极其低级,因此在这不再赘述。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
public class UI extends JFrame {
JTextArea textArea = new JTextArea();
public Container cp = getContentPane();
boolean started = false, stopped = true;
final static chopsticks chopstick = new chopsticks();
final static 哲学家 threadA = new 哲学家(0, "张森", chopstick);
final static 哲学家 threadB = new 哲学家(1, "赵明帅", chopstick);
final static 哲学家 threadC = new 哲学家(2, "张晓天", chopstick);
final static 哲学家 threadD = new 哲学家(3, "张毅", chopstick);
final static 哲学家 threadE = new 哲学家(4, "袁晨", chopstick);
public UI() {
JPanel descipPanel = new JPanel();
JPanel tablePanel = new JPanel();
descipPanel.setBounds(0, 400, 200, 500);
descipPanel.setOpaque(false);
descipPanel.setForeground(Color.red);
descipPanel.setLayout(new BoxLayout(descipPanel, BoxLayout.Y_AXIS));
URL label4url = UI.class.getResource("start.jpg");
Icon label4icon = new ImageIcon(label4url);
URL label1url = UI.class.getResource("think.jpg");
Icon label1icon = new ImageIcon(label1url);
URL label2url = UI.class.getResource("hunger.jpg");
Icon label2icon = new ImageIcon(label2url);
URL label3url = UI.class.getResource("eat.jpg");
Icon label3icon = new ImageIcon(label3url);
JLabel label4 = new JLabel("初始状态的哲学家", label4icon, JLabel.RIGHT);
label4.setForeground(Color.red);
label4.setOpaque(true);
JLabel label1 = new JLabel("思考状态的哲学家", label1icon, JLabel.RIGHT);
label1.setForeground(Color.red);
label1.setOpaque(true);
JLabel label2 = new JLabel("饥饿状态的哲学家", label2icon, JLabel.RIGHT);
label2.setForeground(Color.red);
label2.setOpaque(true);
JLabel label3 = new JLabel("正在吃饭的哲学家", label3icon, JLabel.RIGHT);
label3.setForeground(Color.red);
label3.setOpaque(true);
URL tableurl = UI.class.getResource("philosophy'sDesktop.jpg");
Icon tableicon = new ImageIcon(tableurl);
JLabel table = new JLabel(tableicon);
table.setOpaque(false);
JLabel[] p = new JLabel[5];
p[0] = new JLabel("张森目前状态", threadA.getface(), JLabel.LEFT);
p[0].setForeground(Color.black);
p[1] = new JLabel("照明帅目前状态", threadB.getface(), JLabel.LEFT);
p[1].setForeground(Color.black);
p[2] = new JLabel("张晓天目前状态", threadC.getface(), JLabel.LEFT);
p[2].setForeground(Color.black);
p[3] = new JLabel("张毅目前状态", threadD.getface(), JLabel.LEFT);
p[3].setForeground(Color.black);
p[4] = new JLabel("袁晨目前状态", threadE.getface(), JLabel.LEFT);
p[4].setForeground(Color.black);
JButton start = new JButton("开始");
JButton stop = new JButton("暂停");
JButton continueB = new JButton("继续");
JButton end = new JButton("结束");
Font font = new Font("楷体", Font.BOLD, 20);
JTextField p0 = new JTextField("哲学家\"马顿\"张森目前状态");
p0.setBorder(null);
p0.setFont(font);
p0.setOpaque(false);
JTextField p1 = new JTextField("哲学家\"鸭梨是多德\"张晓天目前状态");
p1.setBorder(null);
p1.setFont(font);
p1.setOpaque(false);
JTextField p2 = new JTextField("哲学家\"柏拉兔\"张毅目前状态");
p2.setBorder(null);
p2.setFont(font);
p2.setOpaque(false);
JTextField p3 = new JTextField("哲学家\"孔纸\"袁晨目前状态");
p3.setBorder(null);
p3.setFont(font);
p3.setOpaque(false);
JTextField p4 = new JTextField("哲学家\"王阳暗\"赵明帅目前状态");
p4.setBorder(null);
p4.setFont(font);
p4.setOpaque(false);
setTitle("哲学家就餐问题模拟器");
setLayout(null);
setResizable(false);
setBounds(0, 0, 1600, 1500);
setExtendedState(JFrame.MAXIMIZED_BOTH);
cp.add(descipPanel);
cp.add(p[0]);
cp.add(p[1]);
cp.add(p[2]);
cp.add(p[3]);
cp.add(p[4]);
cp.add(p0);
cp.add(p1);
cp.add(p2);
cp.add(p3);
cp.add(p4);
tablePanel.add(table);
cp.add(start);
cp.add(stop);
cp.add(continueB);
cp.add(end);
cp.add(tablePanel);
descipPanel.add(label4);
descipPanel.add(label1);
descipPanel.add(label2);
descipPanel.add(label3);
tablePanel.setBounds(500, 0, tableicon.getIconWidth(), tableicon.getIconHeight());
p[0].setBounds(690, 50, threadA.getface().getIconWidth(), threadA.getface().getIconHeight());
p[2].setBounds(1160, 650, threadC.getface().getIconWidth(), threadB.getface().getIconHeight());
p[3].setBounds(530, 650, threadD.getface().getIconWidth(), threadC.getface().getIconHeight());
p[4].setBounds(400, 280, threadE.getface().getIconWidth(), threadD.getface().getIconHeight());
p[1].setBounds(1250, 280, threadB.getface().getIconWidth(), threadE.getface().getIconHeight());
p0.setBounds(590, 115, 250, 50);
p1.setBounds(1165, 720, 370, 50);
p2.setBounds(370, 720, 350, 50);
p3.setBounds(300, 350, 250, 50);
p4.setBounds(1250, 350, 370, 50);
start.setBounds(20, 20, 100, 30);
start.addActionListener(new startlistener());
stop.setBounds(120, 20, 100, 30);
stop.addActionListener(new stoplistener());
continueB.setBounds(220, 20, 100, 30);
continueB.addActionListener(new continuelistener());
end.setBounds(320, 20, 100, 30);
end.addActionListener(new endlistener());
while (true) {
p[0].setIcon(threadA.getface());
p[1].setIcon(threadB.getface());
p[2].setIcon(threadC.getface());
p[3].setIcon(threadD.getface());
p[4].setIcon(threadE.getface());
textArea.setText(this.string(threadA,threadB,threadC,threadD,threadE));
setVisible(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
public String string(哲学家 a,哲学家 b,哲学家 c,哲学家 d,哲学家 e){
String st="\n";
if(a.a!=null)
st+=a.a;
if(a.d!=null)
st+=a.d;
st+="\n";
if(b.a!=null)
st+=b.a;
if(b.d!=null)
st+=b.d;
st+="\n";
if(c.a!=null)
st+=c.a;
if(c.d!=null)
st+=c.d;
st+="\n";
if(d.a!=null)
st+=d.a;
if(d.d!=null)
st+=d.d;
st+="\n";
if(e.a!=null)
st+=e.a;
if(e.d!=null)
st+=e.d;
return st;
}
class startlistener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (!started) {
started = true;
stopped = false;
threadA.start();
threadB.start();
threadC.start();
threadD.start();
threadE.start();
textArea = new JTextArea(threadA.a + threadA.d + "\n" + threadB.a + threadB.d + "\n" + threadC.a + threadC.d + "\n" + threadD.a + threadD.d + "\n" + threadE.a + threadE.d + "\n");
textArea.setOpaque(false);
textArea.setAutoscrolls(true);
Font textarea=new Font("楷体",Font.PLAIN,18);
textArea.setFont(textarea);
textArea.setForeground(Color.MAGENTA);
textArea.setBounds(0, 50, 500, 350);
cp.add(textArea);
}
}
}
class stoplistener implements ActionListener {
@Override
public synchronized void actionPerformed(ActionEvent e) {
if (!stopped) {
started = false;
stopped = true;
synchronized (threadA.c) {
try {
threadA.c.wait();
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
}
synchronized (threadB.c) {
try {
threadB.c.wait();
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
}
synchronized (threadC.c) {
try {
threadC.c.wait();
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
}
synchronized (threadD.c) {
try {
threadD.c.wait();
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
}
synchronized (threadE.c) {
try {
threadE.c.wait();
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
}
}
}
}
class continuelistener implements ActionListener {
@Override
public synchronized void actionPerformed(ActionEvent e) {
if (stopped) {
notifyAll();
stopped = false;
started = true;
}
}
}
class endlistener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(1);
}
}
}
4.最后就是大家期待的运行结果环节啦!可惜CSDN上帖子没法放视频,只能放一些运行时的图片。个人觉得结果差强人意,还算不错啦,最重要的是写代码期间解决了许多问题,学到了许多知识,当然也有问题仍未解决,对于Thread类的wait方法理解还不够。
没办法图片没法完美演示过程,将就一下吧。