1

I was trying to restart a thread after I stop it manually in my Project written in java. When I try to restart it I was give an IllegalThreadStateException error.

I simplified my code as the following sample code. My questions are:

  1. How can I restart the logtest thread, do I have to start a new thread instead of restarting it?

  2. As you can see from the following code, in the run method of the class LogThread, I used Thread.Sleep(3) to output these numbers row by row. If I delete this line, the thread will either die in the middle or output all these 10000 rows all at once. Do I have to force the thread to stop 3 milliseconds to sacrifice its performance? Are there any better way to solve this?

The Code:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.StyledDocument;

public class LogTest {
  private LogThread logtest;
  public LogTest() {
    JFrame frame = new JFrame("LogTest");
    Container con = frame.getContentPane();
    final JTextPane pane = new JTextPane();
    final JScrollPane scollPane = new JScrollPane(pane);
    JButton button = new JButton("Start Test Log");
    JButton button1 = new JButton("Stop Test Log");
    logtest = new LogThread(pane);
    con.add(scollPane);
    con.add(button, BorderLayout.NORTH);
    con.add(button1, BorderLayout.SOUTH);
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        logtest.start();
      }
    });
    button1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        logtest.stop();
      }

    });
    frame.setSize(600, 500);
    frame.setVisible(true);
  }

  public static void main(String[] args) {
    new LogTest();
  }

}

class LogThread implements Runnable {
  private final Thread t;
  private String newline;
  private volatile boolean shouldStop = false;
  private StyledDocument doc;
  private JTextPane pane;
  private static int counter = 0;

  public LogThread(JTextPane pane) {
    this.doc = pane.getStyledDocument();
    this.pane = pane;
    t = new Thread(this, "Log Thread");
    newline = System.getProperty("line.separator");
  }

  public void start() {
    t.start();
  }

  public void run() {
    try {
      if (doc.getLength() > 0) {
        doc.remove(0, doc.getLength());
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    if(shouldStop) shouldStop=false;
    for (int i = counter; i <= 1000 && !shouldStop; i++) {
      try {
        doc.insertString(doc.getLength(),
            i + "-------------------------------------------------------------"+ newline,
            null);
        pane.setCaretPosition(doc.getLength());
        Thread.sleep(3);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  public void stop() {
    shouldStop = true;    
  }
}
phs
  • 10,687
  • 4
  • 58
  • 84
  • You can't. `Thread` is non-reentrant, meaning you can't call `start` on it more than once. You need to create a new `Thread` and pass it a reference to a `Runnable` instance and start that `Thread` – MadProgrammer Jul 08 '14 at 01:32

1 Answers1

3

Basically, you can't, from the JavaDocs...

It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.

What you need to do is, when start is called, is create a new Thread, passing this to it and starting that thread. You will need to determine if a Thread is already running and take action to terminate it first

class LogThread implements Runnable {
  private final Thread t;
  //....

  public LogThread(JTextPane pane) {
      //...
      //t = new Thread(this, "Log Thread");
      //...
  }

  public void start() throws InterruptedException {
    if (t != null && t.isAlive()) {
        shouldStop = true;
        t.join();
    }
    shouldStop = false;
    t = new Thread(this, "Log Thread");
    t.start();
  }

  //...

  public void stop() throws InterruptedException {
    shouldStop = true;    
    t.join();
    t = null;
  }
} 

You are also violating the single thread rules of Swing by modifying the state of the UI from outside the context of the Event Dispatching Thread...

In particular...

pane.setCaretPosition(doc.getLength());

A safer solution would be to use a SwingWorker and update the UI from within then the process method.

Take a look at How to use SwingWorker for more details

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366