I'm a beginner in programing and i'm trying to learn how to do multithreading using the producer consumer model. my first question is if I start the following two threads. do I have to explicitly kill the threads or will the threads end when the code for the thread ends?? In my producer class the run method has no more lines to execute after the user hits the close button.
secondly, I'm not sure why but when I run the program, my deposit method is not being called However, the method and the accompanying print statements are executed in debugging mode. which makes me think that the problem is in the way I set up my monitor(Queue). Any help would be greatly appreciated
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();}
}
here is my producer class
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);
}
}
this the consumer class
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);
}
}
and the Queue(monitor) , the producer class is supposed deposits and the consumer class is supposed to fetch
// this class implements an array queue for storing requests
// it has synchronized methods for inserting and removing requests public class Queue {
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;
}
}
Do I have to explicitly kill the threads or will the threads end when the code for the thread ends?
That depends. If Threads shall stop when the virtual machine exits, you can declare these threads as demon threads. The virtual machine terminates automatically when the last non-demon thread ends. This means that non-demon threads do not keep the virtual machine from exiting, while normal threads do.
If Threads shall stop when a certain event happens, the Thread needs to be stopped in a different way. Here are a few possibilities:
Thread.stop()
on that Thread. Not recommended! This might leave the system in an inconsistent state. Thread.interrupt()
. null
or LastEvent
so that the Consumer knows it should end. Mind you: If you access fields from different Threads, it's strongly recommended to declare them volatile
. In your case, it's the volatile
that's missing, which keeps the Producer thread from noticing that the Event Thread created a Request. volatile
is missing for the boolean guards in both, Producer
and Consumer
.
In your case, there are three Threads involved, and your application's threading model is seriously flawed. The three Threads are:
It should puzzle you that the new Request()
(this looks like "Production", doesn't it?) code is actually not inside the Producer Thread. It is in class Producer
, but it is in the actionPerformed()
call tree, which means it runs on the JVM Event Thread.
The Thread which you call Producer is not really the Producer, it is just a Queuer, it takes the produced Request and queues it. And here lies a problem with your code. If actionPerformed()
is called twice because the user is extremely fast, it could be that one of the user's requests gets lost.
Your Producer Thread is, by the way, busy polling ( bad!!! ). Actually, the entire Producer Thread is redundant, as the Thread that you need for production is already there - it's the Event Thread. Of course you don't want to Event Thread to "stall" in case the queue is full - but there exactly lies another problem with your application, anyhow the way it is done right now it would loose Requests, either if the user is faster than the scheduler or if the Queue gets full.
Your Consumer Thread is also busy polling ( bad!!! ). As long as userReady
is not true, it keeps looping infinitely in run()
without waiting for anything.
So, your Producer and your Consumer Thread together cause 200% CPU consumption when the application is started. Actually, they should cause 0% CPU consumption because when the application is started, there isn't anything to do yet.
Actually for what this application seems to be doing, ie one window is queuing requests and the other is taking requests, you don't need any additional Threads at all, you only need a Queue, that's it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.