结束一个java线程

时间:2014-11-30 02:27:17

标签: java multithreading

我是编程的初学者,我正在尝试学习如何使用生产者消费者模型进行多线程处理。我的第一个问题是,如果我开始以下两个线程。我必须显式杀死线程或线程的代码结束时线程结束?在我的生成器类中,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;
}

}

1 个答案:

答案 0 :(得分:0)

  

我必须显式地终止线程,还是线程代码结束时线程会结束?

这取决于。如果线程在虚拟机退出时停止,则可以将这些线程声明为恶魔线程。当最后一个非恶魔线程结束时,虚拟机会自动终止。这意味着非恶魔线程不会让虚拟机退出,而普通线程则不会。

如果线程在某个事件发生时停止,则需要以不同的方式停止线程。以下是一些可能性:

  • 在该线程上调用Thread.stop()不推荐!这可能会使系统处于不一致状态。
  • 使用Thread.interrupt()
  • 中断线程
  • 如果消费者正在消费事件,请使用nullLastEvent,以便消费者知道它应该结束。
  • 设置消费者定期查询的布尔变量。这可能很棘手,如果布尔变量可以由Producer更改,并且生产者可以在排队事件之前这样做,并且布尔变量不应绕过任何等待机制,通常只能立即使用。
  • 如果没有等待机制并且要使用布尔变量,则应使用wait / notify保护其使用,以便期望状态更改的线程不需要无限拉动但可以等到通知。 / LI>

请注意:如果您访问不同主题的字段,强烈建议您声明它们volatile。在您的情况下,它是volatile缺少的,它使生产者线程不会注意到事件线程创建了一个请求。 volatileProducer中的布尔警卫都缺少Consumer

在您的情况下,涉及三个线程,您的应用程序的线程模型存在严重缺陷。三个主题是:

  • JVM事件线程
  • 制作人线程
  • 消费者线程

new Request()(这看起来像“生产”,不是吗?)应该让你感到困惑。)代码实际上不在生产者线程中。它位于class Producer中,但它位于actionPerformed()调用树中,这意味着它在JVM事件线程上运行。

您调用Producer的线程实际上不是Producer,它只是一个Queuer,它会生成请求并对其进行排队。这就是你的代码存在问题。如果actionPerformed()被调用两次因为用户非常快,则可能是用户的一个请求丢失了。

顺便说一下,你的制作人线程是忙碌的民意调查糟糕!!! )。 实际上,整个Producer Thread是多余的,因为生产所需的Thread已经存在 - 它是Event Thread。当然,如果队列已满,你不希望事件线程“停顿” - 但是你的应用程序确实存在另一个问题,无论如何它现在就完成它会松散请求,如果用户更快的话比调度程序或队列满了。

您的消费者线程也忙于轮询错误!!! )。只要userReady不成立,它就会在run()中无限循环而无需等待任何事情。

因此,生产者和消费者线程一起在应用程序启动时会导致200%的CPU消耗。实际上,它们应该导致0%的CPU消耗,因为当应用程序启动时,还没有任何事情要做。

实际上对于这个应用程序似乎正在做什么,即一个窗口正在排队请求而另一个窗口正在接收请求,你根本不需要任何额外的线程,你只需要一个队列,就是这样。