我是编程的初学者,我正在尝试学习如何使用生产者消费者模型进行多线程处理。我的第一个问题是,如果我开始以下两个线程。我必须显式杀死线程或线程的代码结束时线程结束?在我的生成器类中,run方法在用户点击关闭按钮后不再执行任何行。
其次,我不确定为什么,但是当我运行程序时,我的存款方法没有被调用 但是,该方法和附带的打印语句在调试模式下执行。这让我觉得问题出在我设置监视器(队列)的方式上。任何帮助将不胜感激
public class Main {
public Main() {
Queue buffer = new Queue();
Producer producer = new Producer(buffer);
Thread producerThread = new Thread(producer);
Consumer consumer = new Consumer(buffer);
Thread consumerThread = new Thread(consumer);
producerThread.start();
consumerThread.start();
}
public static void main (String[] args){ new Main();}
}
这是我的制作人类
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class Producer extends JFrame implements ActionListener, FocusListener, Runnable {
private Request request; // new request to be added
private Queue buffer; // buffer to add requests to
private JLabel dateLabel;
private JTextField dateField; // Field to hold date
private JLabel timeLabel;
private JTextField timeField; // Field to hold time
private JLabel requestLabel;
private JTextField requestField; // Field to hold request number
private JLabel textLabel;
private JTextArea textArea; // Text area to describe issue
private JButton okButton; // accept button
private JButton closeButton; // button to close form
private Boolean dateFlag; // flag to check date field has valid input
private Boolean timeFlag; // flag to check time field has valid input
private Boolean requestFlag; // flag to check request field has valid input
private boolean closed; // flag to indicate if user closed form
private boolean requestReady;
// constructor
public Producer (Queue q){
buffer = q;
closed = false;
requestReady = false;
setUpForm();
}
public void run() {
System.out.println("run called");
while (!closed){
if (requestReady) {
System.out.println("request is ready");
buffer.deposit(request);
System.out.println("deposit done");
requestReady = false;
}
}
}
// catches action event when enter is pressed in a field or a button is pressed
public void actionPerformed(ActionEvent e) {
if(e.getSource() == dateField){
DateFormat t = DateFormat.getDateInstance(DateFormat.SHORT);
try {
t.parse(dateField.getText());
dateField.setForeground(java.awt.Color.BLACK);
dateFlag = true;
} catch (ParseException e1) {
dateField.setForeground(java.awt.Color.RED);
dateFlag = false;
}
}
if(e.getSource() == timeField){
DateFormat t = DateFormat.getTimeInstance(DateFormat.SHORT);
try {
t.parse(timeField.getText());
timeField.setForeground(java.awt.Color.BLACK);
timeFlag = true;
} catch (ParseException e1) {
timeField.setForeground(java.awt.Color.RED);
timeFlag = false;
}
}
if(e.getSource() == requestField){
NumberFormat t = NumberFormat.getIntegerInstance();
try {
t.parse(requestField.getText());
requestField.setForeground(java.awt.Color.BLACK);
requestFlag = true;
} catch (ParseException e1) {
requestField.setForeground(java.awt.Color.RED);
requestFlag = false;
}
}
if(e.getSource() == okButton){
if (dateFlag && timeFlag && requestFlag){
request = new Request(dateField.getText(), timeField.getText(), requestField.getText(), textArea.getText());
requestReady = true;
dateField.setText(null);
timeField.setText(null);
requestField.setText(null);
textArea.setText(null);
dateFlag = false;
timeFlag = false;
requestFlag = false;
}
}
if(e.getSource() == closeButton){
closed = true;
System.exit(0);
Thread.currentThread().interrupt();
return;
}
}
// Unsupported method, not needed
public void focusGained(FocusEvent e) {
}
// Method catches event when focus is lost from a field
public void focusLost(FocusEvent e) {
if(e.getSource() == dateField){
DateFormat t = DateFormat.getDateInstance(DateFormat.SHORT);
try {
t.parse(dateField.getText());
dateField.setForeground(java.awt.Color.BLACK);
dateFlag = true;
} catch (ParseException e1) {
dateField.setForeground(java.awt.Color.RED);
dateFlag = false;
}
}
if(e.getSource() == timeField){
DateFormat t = DateFormat.getTimeInstance(DateFormat.SHORT);
try {
t.parse(timeField.getText());
timeField.setForeground(java.awt.Color.BLACK);
timeFlag = true;
} catch (ParseException e1) {
timeField.setForeground(java.awt.Color.RED);
timeFlag = false;
}
}
if(e.getSource() == requestField){
NumberFormat t = NumberFormat.getIntegerInstance();
try {
t.parse(requestField.getText());
requestField.setForeground(java.awt.Color.BLACK);
requestFlag = true;
} catch (ParseException e1) {
requestField.setForeground(java.awt.Color.RED);
requestFlag = false;
}
}
}
// this method readies the form, adds the fields, adds listeners
private void setUpForm(){
this.setSize(500,800);
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // set the default action taken when form is closed
this.setLayout(new BorderLayout()); // use layout manager to set layout
dateLabel = new JLabel("Date");
dateField = new JTextField(10);
timeLabel = new JLabel("Time");
timeField = new JTextField(10);
requestLabel = new JLabel("Request#");
requestField = new JTextField(10);
textLabel = new JLabel("Issue");
textArea = new JTextArea(5,40);
textArea.setLineWrap(isEnabled());
okButton = new JButton("Ok");
closeButton = new JButton("Close");
JPanel jp1 = new JPanel();
jp1.setLayout(new GridLayout(0,4));
jp1.add(dateLabel);
jp1.add(dateField);
jp1.add(timeLabel);
jp1.add(timeField);
jp1.add(requestLabel);
jp1.add(requestField);
add(jp1, BorderLayout.NORTH);
JPanel jp2 = new JPanel();
jp2.setLayout(new FlowLayout());
jp2.add(textLabel);
jp2.add(textArea);
add(jp2, BorderLayout.CENTER);
JPanel jp3 = new JPanel();
jp3.setLayout(new FlowLayout());
jp3.add(okButton);
jp3.add(closeButton);
add(jp3, BorderLayout.SOUTH);
dateFlag = false;
timeFlag = false;
requestFlag = false;
timeField.addActionListener(this);
dateField.addActionListener(this);
requestField.addActionListener(this);
timeField.addFocusListener(this);
dateField.addFocusListener(this);
requestField.addFocusListener(this);
okButton.addActionListener(this);
closeButton.addActionListener(this);
this.pack();
this.setVisible(true);
}
}
这是消费者阶层
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
class Consumer extends JFrame implements ActionListener, Runnable {
private Queue buffer;
private boolean closed;
private JLabel dateLabel;
private JTextField dateField; // Field to hold date
private JLabel timeLabel;
private JTextField timeField; // Field to hold time
private JLabel requestLabel;
private JTextField requestField; // Field to hold request number
private JLabel textLabel;
private JTextArea textArea; // Text area to describe issue
private JButton okButton; // accept button
private JButton closeButton; // button to close form
private boolean userReady;
public Consumer(Queue que) {
buffer = que;
userReady = false;
closed = false;
setUpForm();
}
public void run() {
Request r;
while (!closed) {
if (userReady){
r = buffer.fetch();
dateField.setText(r.getDate());
dateField.setText(r.getTime());
requestField.setText(r.getRequestNumber());
textArea.setText(r.getIssue());
userReady = false;
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == okButton){
//System.out.println(" ok pressed");
userReady = true;
}
if(e.getSource() == closeButton){
closed = true;
System.exit(0);
Thread.currentThread().interrupt();
return;
}
}
private void setUpForm() {
this.setTitle("Consumer");
this.setLocation(700, 30);
this.setAlwaysOnTop(true);
this.setSize(500,800);
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // set the default action taken when form is closed
this.setLayout(new BorderLayout()); // use layout manager to set layout
dateLabel = new JLabel("Date");
dateField = new JTextField(10);
timeLabel = new JLabel("Time");
timeField = new JTextField(10);
requestLabel = new JLabel("Request#");
requestField = new JTextField(10);
textLabel = new JLabel("Issue");
textArea = new JTextArea(5,40);
textArea.setLineWrap(isEnabled());
okButton = new JButton("Ok");
closeButton = new JButton("Close");
JPanel jp1 = new JPanel();
jp1.setLayout(new GridLayout(0,4));
jp1.add(dateLabel);
jp1.add(dateField);
jp1.add(timeLabel);
jp1.add(timeField);
jp1.add(requestLabel);
jp1.add(requestField);
add(jp1, BorderLayout.NORTH);
JPanel jp2 = new JPanel();
jp2.setLayout(new FlowLayout());
jp2.add(textLabel);
jp2.add(textArea);
add(jp2, BorderLayout.CENTER);
JPanel jp3 = new JPanel();
jp3.setLayout(new FlowLayout());
jp3.add(okButton);
jp3.add(closeButton);
add(jp3, BorderLayout.SOUTH);
timeField.addActionListener(this);
dateField.addActionListener(this);
requestField.addActionListener(this);
okButton.addActionListener(this);
closeButton.addActionListener(this);
this.pack();
this.setVisible(true);
}
}
和队列(监视器),生产者类应该是存款而消费者类应该获取
// this class implements an array queue for storing requests
//它具有用于插入和删除请求的同步方法 公共类队列{
private Request[] queue;
private int nextEmptyIndex;
private int nextFullIndex;
private int fullSpots;
private int queueSize;
public Queue(){
queue = new Request[5];
nextEmptyIndex = 1;
nextFullIndex = 1;
fullSpots = 0;
queueSize = 5;
}
public synchronized void deposit(Request r){
try {
System.out.println("Deposit Called");
while (fullSpots == queueSize){
wait();
}
queue[nextEmptyIndex] = r;
nextEmptyIndex = (nextEmptyIndex % queueSize) + 1;
fullSpots++;
System.out.println("deposited");
notifyAll();
}
catch(InterruptedException e){
}
}
public synchronized Request fetch () {
Request r = null;
try {
while (fullSpots == 0){
wait();
}
r = queue[nextFullIndex];
nextFullIndex = (nextFullIndex % queueSize) +1;
fullSpots--;
notifyAll();
}
catch(InterruptedException e){
}
return r;
}
}
答案 0 :(得分:0)
我必须显式地终止线程,还是线程代码结束时线程会结束?
这取决于。如果线程在虚拟机退出时停止,则可以将这些线程声明为恶魔线程。当最后一个非恶魔线程结束时,虚拟机会自动终止。这意味着非恶魔线程不会让虚拟机退出,而普通线程则不会。
如果线程在某个事件发生时停止,则需要以不同的方式停止线程。以下是一些可能性:
Thread.stop()
。 不推荐!这可能会使系统处于不一致状态。Thread.interrupt()
。null
或LastEvent
,以便消费者知道它应该结束。请注意:如果您访问不同主题的字段,强烈建议您声明它们volatile
。在您的情况下,它是volatile
缺少的,它使生产者线程不会注意到事件线程创建了一个请求。 volatile
和Producer
中的布尔警卫都缺少Consumer
。
在您的情况下,涉及三个线程,您的应用程序的线程模型存在严重缺陷。三个主题是:
new Request()
(这看起来像“生产”,不是吗?)应该让你感到困惑。)代码实际上不在生产者线程中。它位于class Producer
中,但它位于actionPerformed()
调用树中,这意味着它在JVM事件线程上运行。
您调用Producer的线程实际上不是Producer,它只是一个Queuer,它会生成请求并对其进行排队。这就是你的代码存在问题。如果actionPerformed()
被调用两次因为用户非常快,则可能是用户的一个请求丢失了。
顺便说一下,你的制作人线程是忙碌的民意调查(糟糕!!! )。 实际上,整个Producer Thread是多余的,因为生产所需的Thread已经存在 - 它是Event Thread。当然,如果队列已满,你不希望事件线程“停顿” - 但是你的应用程序确实存在另一个问题,无论如何它现在就完成它会松散请求,如果用户更快的话比调度程序或队列满了。
您的消费者线程也忙于轮询(错误!!! )。只要userReady
不成立,它就会在run()
中无限循环而无需等待任何事情。
因此,生产者和消费者线程一起在应用程序启动时会导致200%的CPU消耗。实际上,它们应该导致0%的CPU消耗,因为当应用程序启动时,还没有任何事情要做。
实际上对于这个应用程序似乎正在做什么,即一个窗口正在排队请求而另一个窗口正在接收请求,你根本不需要任何额外的线程,你只需要一个队列,就是这样。