本程序原作链接:https://blog.csdn.net/GodOuO/article/details/105879961
(拜谢)
本程序基于原文程序完成,原程序会出现缓冲池限制不起作用,导致生产者一直放的问题,所以对程序进行了重构,加了一个显示生产者消费者状态的界面。
直接按照这个结构复制文中的代码可以直接运行。
- 程序文件结构
- 运行截图
- 源码
MyFrame
package myframe;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import thread.P_C;
public class MyFrame extends JFrame{
private static final long serialVersionUID = 1222538739701278727L;
//主界面的各个画板
@SuppressWarnings("unused")
private JPanel toprightpanel,topleftpanel,midpanel,buttonpanel;
//中间显示区画板
public static MyShowPanel showpanel;
private JButton beginbutton;
private static JTextArea textcout;
private JScrollPane scrollpane;
//选择下拉框
private JComboBox<String> catch_choice;
private JComboBox<String> pro_choice;
private JComboBox<String> con_choice;
//生产,消费,缓冲队列
public static LinkedList<String> pro_list;
public static LinkedList<String> con_list;
public static LinkedList<String> catch_list;
//缓冲池大小,生产者数量,消费者数量
public static int catch_num,pro_num,con_num;
//缓冲池状态
private static boolean empty;
private static boolean full;
//子窗口显示生产者消费者状态
private static JFrame subFrame;
//显示生产者消费者的画板
public static sumpanel subPanel;
//生产者消费者的状态数组
public static boolean[] prostate;
public static boolean[] constate;
/**
* @return
* empty getter
*/
public static boolean isempty() {
return empty;
}
/**
* @param is
* empty setter
*/
public static void setempty(boolean is) {
empty=is;
}
/**
* @return
* full getter
*/
public static boolean isfull() {
return full;
}
/**
* @param is
* full setter
*/
public static void setfull(boolean is) {
full=is;
}
/**
* 清空界面和状态
*/
public void clear() {
pro_list.clear();
catch_list.clear();
con_list.clear();
setempty(false);
setfull(false);
}
//字体
private static Font font=new Font("宋体", Font.PLAIN, 20);
//静态初始化
static {
empty=false;
full=false;
pro_num=100;
pro_list=new LinkedList<>();
con_list=new LinkedList<>();
catch_list=new LinkedList<>();
}
MyFrame(){
this.setFont(font);
//初始化各组件信息
topleftpanel();
scrollpane.setBounds(20, 10, 350, 170);
toprightpanel();
toprightpanel.setBounds(400, 10, 350, 170);
midlabel();
midpanel.setBounds(20, 190, 800, 20);
lowpanel();
buttonpanel.setBounds(20,580,750,40);
showpanel=new MyShowPanel();
showpanel.setBounds(0,220,770,350);
subFrame=new JFrame();
//添加组件
this.setLayout(null);
this.add(toprightpanel);
this.add(scrollpane);
this.add(midpanel);
this.add(buttonpanel);
this.add(showpanel);
//设置窗口属性
setSize(770, 680);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);//窗口大小不可变
setLocation(200,200);
}
/**
* @param string
* 输出文字信息
*/
public static void showInfo(String string) {
textcout.append(string);
//自动跟随文字
textcout.setCaretPosition(textcout.getDocument().getLength());
}
/**
* 创建顶部右侧选择画板
*/
private void toprightpanel() {
JLabel toprightprolabel,toprightcatchlabel,toprightconlabel;
String []numberStr= {"0","1","2","3","4","5","6","7","8","9","10"};
toprightprolabel=new JLabel();
toprightprolabel.setText("生产者数量");
toprightprolabel.setBounds(5,5,80,20);
pro_choice=new JComboBox<String>(numberStr);
pro_choice.setBounds(90,5,60,20);
JPanel pane1=new JPanel();
pane1.setLayout(null);
pane1.add(toprightprolabel);
pane1.add(pro_choice);
toprightconlabel=new JLabel();
toprightconlabel.setText("消费者数量");
toprightconlabel.setBounds(5, 5,80, 20);
con_choice=new JComboBox<String>(numberStr);
con_choice.setBounds(90,5,60,20);
JPanel pane2=new JPanel();
pane2.setLayout(null);
pane2.add(toprightconlabel);
pane2.add(con_choice);
toprightcatchlabel=new JLabel();
toprightcatchlabel.setText("缓冲池大小");
toprightcatchlabel.setBounds(5, 5,80, 20);
catch_choice=new JComboBox<String>(numberStr);
catch_choice.setBounds(90,5,60,20);
JPanel pane3=new JPanel();
pane3.setLayout(null);
pane3.add(toprightcatchlabel);
pane3.add(catch_choice);
toprightpanel=new JPanel();
toprightpanel.setBorder(BorderFactory.createTitledBorder(null, "选择", TitledBorder.LEFT, TitledBorder.TOP));
toprightpanel.setLayout(new BoxLayout(toprightpanel,BoxLayout.Y_AXIS));
toprightpanel.add(pane1);
toprightpanel.add(pane2);
toprightpanel.add(pane3);
}
/**
* 创建顶部左侧画板
*/
private void topleftpanel() {
textcout=new JTextArea(20,10);
textcout.setEditable(false);
scrollpane=new JScrollPane();
scrollpane.setViewportView(textcout);
scrollpane.setColumnHeaderView(new JLabel("文字显示"));
textcout.setFont(font);
}
/**
* 中间label
*/
private void midlabel() {
JLabel prolabel,catchlabel,conlabel;
prolabel=new JLabel();
conlabel=new JLabel();
catchlabel=new JLabel();
prolabel.setText("生产者");
conlabel.setText("消费者");
catchlabel.setText("缓冲池");
prolabel.setBounds(35,5,200,20);
conlabel.setBounds(535,5,200,20);
catchlabel.setBounds(285,5,200,20);
midpanel=new JPanel();
midpanel.setLayout(null);
midpanel.add(prolabel);
midpanel.add(conlabel);
midpanel.add(catchlabel);
}
/**
* 底部按钮
*/
private void lowpanel() {
beginbutton=new JButton();
beginbutton.setText("开始");
beginbutton.setBounds(310,1,70,35);
beginbutton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(e.getSource()==beginbutton) {
//子窗口
pro_num=Integer.valueOf((String)pro_choice.getSelectedItem());
catch_num=Integer.valueOf((String)catch_choice.getSelectedItem());
con_num=Integer.valueOf((String)con_choice.getSelectedItem());
prostate=new boolean[pro_num];
constate=new boolean[con_num];
setstate();
subPanel=new sumpanel(pro_num,con_num);
subFrame.setLayout(null);
subPanel.setBounds(5, 5, 305, 305);
subFrame.setSize(400,400);
subFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
subFrame.add(subPanel);
subFrame.setLocation(200,200);
subFrame.setVisible(true);
clear();
textcout.setText("");
P_C pc=new P_C();
pc.setThreadnum(pro_num, con_num,catch_num);
pc.run();
}
}
});
buttonpanel=new JPanel();
buttonpanel.setLayout(null);
buttonpanel.add(beginbutton);
}
public static void main(String[]args) {
@SuppressWarnings("unused")
MyFrame a=new MyFrame();
}
/**
* 初始化消费者生产者状态
*/
public void setstate() {
for(int i=0;i<pro_num;i++) {
prostate[i]=false;
}
for(int i=0;i<con_num;i++) {
constate[i]=false;
}
}
public class MyShowPanel extends JPanel{
/**
*
*/
private static final long serialVersionUID = 4590517425748077768L;
MyShowPanel(){
this.setBackground(Color.WHITE);
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.blue);
for(int i=0;i<pro_list.size();i++) {
g.fill3DRect(35, i*20+5, 200, 20, true);
g.drawString(pro_list.get(i), 20, i*20+15);
}
if(isfull()) {
//若公共缓冲池已满,将公共缓冲池里的缓冲区变为红色
g.setColor(Color.RED);
}
g.setColor(Color.gray);
for(int i=0;i<catch_list.size();i++) {
g.fill3DRect(285, i*20+5, 200, 20, true);
g.drawString(catch_list.get(i), 270, i*20+15);
}
g.setColor(Color.yellow);
for(int i=0;i<con_list.size();i++) {
g.fill3DRect(535, i*20+5, 200, 20, true);
g.drawString(con_list.get(i), 520, i*20+15);
}
if(isempty()) {
//若公共缓冲池为空
g.setColor(Color.black);
g.fill3DRect(285, 5, 200, 200, true);
g.setColor(Color.black);
g.drawString("空", 385, 100);
}
}
}
@SuppressWarnings("serial")
public class sumpanel extends JPanel{
private int pro_num,con_num;//pro_num是生产者数量,con_num是消费者数量
sumpanel(int pro,int con){
setLayout(null);
pro_num=pro;
con_num=con;
//子窗口消费者生产者label数组
JLabel[] prolabel=new JLabel[pro];
JLabel[] conlabel=new JLabel[con];
for(int i=0;i<pro;i++) {
prolabel[i]=new JLabel();
prolabel[i].setText((i+1)+"号生产者");
prolabel[i].setBounds(5, 5+20*i, 100,20 );
add(prolabel[i]);
}
for(int j=0;j<con;j++) {
conlabel[j]=new JLabel();
conlabel[j].setText((j+1)+"号消费者");
conlabel[j].setBounds(180, 5+20*j, 100, 20);
add(conlabel[j]);
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.green);
for(int i=0;i<pro_num;i++) {
if(prostate[i]==true) {
g.fill3DRect(110,5+20*i,10,10,true);
}
}
g.setColor(Color.green);
for(int i=0;i<con_num;i++) {
if(constate[i]) {
g.fillOval(285, 5+20*i, 10, 10);
}
}
}
}
}
producer
package thread;
import java.util.Random;
import myframe.MyFrame;
public class producer extends Thread{
private String name;
producer(){
this.name=String.valueOf(P_C.namepro++);
}
@Override
public void run() {
while(P_C.getproduceive()>0) {
P_C.lock.lock();
try {
sleep(3000);
//缓冲池满则阻塞
while(MyFrame.catch_list.size()==P_C.catchMAX) {
//输出线程文字信息
MyFrame.showInfo("警告:Full! "+name+"号生产者受阻,被阻塞!\n");
MyFrame.setfull(true);
//生产者被阻塞,更新其状态
MyFrame.prostate[Integer.parseInt(name)-1]=false;
//更新后调用repaint()刷新画面
MyFrame.subPanel.repaint();
MyFrame.showpanel.repaint();
P_C.full.await();
//生产者被唤醒,更新状态
MyFrame.prostate[Integer.parseInt(name)-1]=true;
//调用repaint()刷新画面
MyFrame.subPanel.repaint();
//输出线程文字信息
MyFrame.showInfo(this.name+"号生产者开始\n");
}
//开始生产
MyFrame.prostate[Integer.parseInt(name)-1]=true;
MyFrame.subPanel.repaint();
//从生产队列中取走
String str=MyFrame.pro_list.removeLast();
if(MyFrame.catch_list.add(str)) {
MyFrame.showInfo(name+"号生产者生产: "+str+"\n");
MyFrame.setempty(false);
MyFrame.showpanel.repaint();
P_C.empty.signal();
}else {
P_C.full.await();
}
}catch (InterruptedException ie) {
//捕获到异常说明进程被异常中断
MyFrame.showInfo("生产异常终止!\n");
} finally{
P_C.lock.unlock();
//减少可生产的数量
P_C.lessproduceive();
try {
//线程随机暂停几秒
sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
MyFrame.showInfo("结束");
}
}
consumer
package thread;
import java.util.Random;
import myframe.MyFrame;
public class consumer extends Thread{
private String name;
consumer(){
this.name=String.valueOf(P_C.namecon++);
}
@Override
public void run() {
while(!(P_C.getproduceive()==0 && MyFrame.catch_list.size()==0)) {
P_C.lock.lock();
try {
sleep(2000);
//缓冲池空则阻塞
while(MyFrame.catch_list.isEmpty()) {
MyFrame.showInfo("警告: Empty!\n"+name+"号消费者受阻!\n");
MyFrame.setempty(true);
//更新消费者状态为false(阻塞)
MyFrame.constate[Integer.parseInt(name)-1]=false;
//刷新画面
MyFrame.showpanel.repaint();
MyFrame.subPanel.repaint();
P_C.empty.await();
//更新消费者状态
MyFrame.constate[Integer.parseInt(name)-1]=true;
//刷新画面
MyFrame.subPanel.repaint();
//输出线程文字信息
MyFrame.showInfo(name+"号消费者开始\n");
}
//开始消费
MyFrame.constate[Integer.parseInt(name)-1]=true;
MyFrame.subPanel.repaint();
//从缓冲池中取走
String str=MyFrame.catch_list.removeLast();
if(MyFrame.con_list.add(str)) {
MyFrame.showInfo(name+"号消费者取走: " + str+"\n");
MyFrame.setfull(false);
MyFrame.showpanel.repaint();
P_C.full.signal();
}
}catch (InterruptedException ie) {
MyFrame.showInfo("消费异常终止!\n");
}finally {
P_C.lock.unlock();
try {
//线程随机暂停几秒
sleep(new Random().nextInt(3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
P_C
package thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import myframe.MyFrame;
public class P_C {
public static Lock lock = new ReentrantLock();
public static Condition full = lock.newCondition();
public static Condition empty = lock.newCondition();
//生产者消费者线程数量
public static int pro_threadnum,con_threadnum;
public static int productive=100;//产品数量
//缓冲池大小
public static int catchMAX;
//生产者名字计数
public static int namepro=1;
//消费者名字计数
public static int namecon=1;
/**
* @return
* productive getter
*/
public static int getproduceive() {
return productive;
}
/**
* 减少可生产数量
*/
public static void lessproduceive() {
productive--;
}
public P_C() {
MyFrame.pro_list.clear();
//初始化生产队列
for(int i=0;i<productive;i++) {
MyFrame.pro_list.add(new String(String.valueOf(i)));
}
}
/**
* @param pro
* @param con
* @param catchmax
* 设置生产者,消费者,缓冲池大小
*/
public void setThreadnum(int pro,int con,int catchmax) {
pro_threadnum=pro;
con_threadnum=con;
catchMAX=catchmax;
}
/**
* 根据生产者,消费者线程数量创建线程
*/
public void run() {
int max,min,decive;
if(pro_threadnum>=con_threadnum) {
max=pro_threadnum;
min=con_threadnum;
decive=max-min;
for(int i=0;i<min;i++) {
new producer().start();
new consumer().start();
}
for(int i=0;i<decive;i++) {
new producer().start();
}
}
else {
min=pro_threadnum;
max=con_threadnum;
decive=max-min;
for(int i=0;i<min;i++) {
new producer().start();
new consumer().start();
}
for(int i=0;i<decive;i++) {
new consumer().start();
}
}
namepro=1;
namecon=1;
}
}