Java kill or terminate a thread
Hi all: Basically I need to kill or stop a thread running when user press a Terminate button. This thread loop through a arraylist and display each event on JTextArea. The requirement is when user press the Terminate button, I need to terminate the running thread and at the same time ADD a new "Terminating" event to the arraylist and let it run again to print "Programing terminating". The following code kind of "works", but I got a java.util.ConcurrentModificationException in the console. Anyone can help?
public void startEvents()
{
terminate = false;
worker = new Thread(new Runnable()
{
public void run()
{
Iterator<Event> it = eventList.iterator();
whi开发者_开发问答le (it.hasNext())
{
waitWhileSuspended();
terminatEvents();
Event ev = it.next();
try
{
Thread.sleep(ev.getDelayTime());
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
jTextArea.append(ev.toString() + "\n");
it.remove();
}
jbStart.setEnabled(true);
jmiStart.setEnabled(true);
jbRestart.setEnabled(true);
jmiRestart.setEnabled(true);
}
});
worker.start();
}
public void terminatEvents()
{
while(terminate)
{
Thread.yield();
eventList.clear();
eventList.add(new Terminate(delayTime));
startEvents();
}
}
The issue is that you are modifying a List and at the same time looping over it. With standard Lists the behaviour of this is undefined and this throws the Exception. Have a look at the java.util.concurrent package for collections that are safe for multi threaded use.
It looks like you are modifying the list (clearing it then adding a new Terminate
event) while iterating on it. That's why you get the ConcurrentModificationException
.
I would advise you to simply have a terminate()
method in your thread object, and call it to stop printing event the list THEN print the new Terminate
event, without using the list.
You have a changing of Collection from 2 threads. By default, collections are unsynchronized, you should use "Synchronized" keyword or switch to synchronizedCollection http://download.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#synchronizedCollection(java.util.Collection)
The usual way to stop a thread is to set some volatile boolean flag, I see that in your case it is the terminate
field. To follow the normal pattern, you should just check this flag on each iteration like while (!terminated && ...)
. The thread that sets the terminate flag should also put your final event in some field, say, terminateEvent which you should check after the loop if terminate is true at that point (that is, if the thread was terminated as opposed to finishing normally). Of course, access to terminateEvent should be synchronized (note that volatile probably won't work here).
However, since you have a list of events to process, I would rather follow another pattern. Replace the list with a concurrent queue (LinkedBlockingQueue is a good example) and then, when you need to terminate the thread, instead of setting a boolean flag you just clear the queue and put your termination event there. The event processing thread, after processing each event, should check if that was a termination event (by using instanceof or some sort of getEventClass() method) and if it was, just break the loop.
Note that since your thread has lengthy operations like Thread.sleep() and possibly waitWhileSuspended() (whatever it is, although you may not need it any more after switching to a blocking queue), you need to interrupt() your thread after placing the termination event into the queue, and handle InterruptedException in the event processing thread accordingly to the application logic. For example, you should decide whether to process an event if Thread.sleep() was interrupted or maybe continue to the next iteration instead.
The way I would do it is
public void run() {
while (!isInterrupted()) {
// actual working code goes here
}
} // end of life for this thread
and then just call interrupt() when I want to stop the thread.
精彩评论