简体   繁体   中英

ending a java thread

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:

  • Calling Thread.stop() on that Thread. Not recommended! This might leave the system in an inconsistent state.
  • Interrupting the Thread using Thread.interrupt() .
  • If the Consumer is consuming events, use null or LastEvent so that the Consumer knows it should end.
  • Set a boolean variable that is queried regularly by the Consumer. This can be tricky and usually only works straight away if the boolean variable can be changed by the Producer and the Producer can do so before queuing the event, and the boolean variable should not bypass any wait mechanisms.
  • If there are no wait mechanisms and a boolean variable is to be used, its use should be guarded with wait / notify so that a Thread that expects a state change doesn't need to pull infinitely but can wait until notified.

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:

  • JVM Event Thread
  • Producer Thread
  • Consumer Thread

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM