How to stop a Thread(怎样停止一个线程)

Abstract

How to stop a Thread is a perannual question for Java programmers. Finally with the release of Java V5.0 (or V1.5), which incorporates java.util.concurrent, a definitive answer can be given.
The answer is you stop a thread by using interrupt (). This article covers some of the background to stopping threads and the suggested alternatives and discusses why interrupt () is the answer. It also discusses interrupting blocking I / O and what else the
programmer needs to do to ensure their threads and tasks are stoppable.

Introduction

One of Java‘s claims to fame is that it was conceived from the start as a language that would support multi-threaded programming. Threads are a first class object and the synchronized keyword
was provided to assist in co-ordinating threads. Unfortunately as one of the first (the first?) languages to incorporate threads into its design, Java‘s designers did not get everything right the first time round.

In particular, they failed in their attempts to provide a safe and effective means of stopping a thread once it had started. The original java.lang.Thread class included the methods, start
(), stop (), stop (Throwable) and suspend (), destroy () and resume (), which were intended to provide the basic functionality for starting and stopping a thread. Of these only the start () method has not been depreciated. The javadoc comments for the other
methods warn against their use .

The Sun article, Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?, Goes into some detail as to why these methods were deprecated (Note: destroy () has now also been depreciated).
Some of that discussion will be covered below.

The problems with stopping Threads were not the only problems the previous versions of Java had. There was also no straight forward mechanism to catch and handle any exception the thread
might throw out of its run method. This lead to poor coding like

public void run () {

/ / Note the Thread.run () method is declared

/ / NOT to throw any checked exceptions

try {

....

/ / Some code here which might throw

/ / A checked exception such as an IOException

..

} Catch (IOException ioex) {

ioex.printStackTrace ();

}

}

because it was not easy to let the rest of the program know that the thread had terminated with an exception. A related problem was that of returning the results of a thread‘s action. Typically
these results had to be stored in some synchronized variable and then the thread had to notify the caller when the results were valid. For Java V1.4 and below, my ThreadReturn package provided a solution to both these problems.

Other problems with multi-threaded programming in previous versions of Java include a lack of common robust utilities to do such things as,

*

synchronize two threads at a point and exchange objects

*

a counting semaphore to control / restrict concurrent access to a resource (synchronized limits access to just one thread at a time)

*

a countdown latch that blocks one or more threads until a set of operations performed by other threads completes

*

read / write locks which allow any number of thread to read but only one thread to write.

*

atomic updates for Integer, Long etc which allow a value to be set in a thread safe lock-free manner.

Java V5.0 incorporates Doug Lee‘s Concurrent package as java.util.concurrent. This package provides robust utilities for the above items. You are encouraged to read the javadocs carefully
and make use of these utilities. Since this article is primarily about stopping Threads it will not go into any more detail on these utilities.

Suggested Methods for Stopping a Thread.

Now that the Thread‘s stop (), suspend () etc., Methods have been deprecated, the only way to safely terminate a thread is to have it exit its run () method, perhaps via an un-checked exception.
In Sun‘s article, Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?, there are some suggestions for how to stop a thread without using the unsafe deprecated methods.

The method suggested there is to use a volatile stop flag (blinker in the code below)

private volatile Thread blinker;

public void stop () {

blinker = null;

}

public void run () {

Thread thisThread = Thread.currentThread ();

while (blinker == thisThread) {

try {

thisThread.sleep (interval);

} Catch (InterruptedException e) {

}

repaint ();

}

}

The volatile keyword is used to ensure prompt communication between threads. "A field may be declared volatile, in which case a thread must reconcile its working copy of the field with the
master copy every time it accesses the variable. Moreover, operations on the master copies of one or more volatile variables on behalf of a thread are performed by the main memory in exactly the order that the thread requested. "(See http://java.sun.com/docs/books/jls/second_edition/html/
classes.doc.html # 36930 for more details.

Synchronization, Volatile and Atomic Variables

In general, multi-threaded access to non-atomic variables should be synchronized to avoid invalid values being retrieved. Atomic variables are guaranteed to be updated (saved or loaded)
in one machine instruction and so are always in a valid state regardless of the number of threads accessing them. Note however due to caching the valid state may not be the most up todate one. What are atomic variables varies from architecture to architecture
(32 versus 64 bit for example). In general primitive data types with sizes smaller then the machine word length are atomic. On most (all) architectures these include, char, byte, short, int, boolean.

However "The load, store, read, and write actions on volatile variables are atomic, even if the type of the variable is double or long." Http://java.sun.com/docs/books/jls/second_edition/html
/ memory.doc.html # 28733

So declaring the Thread variable, blinker, as volatile makes its update atomic and hence synchronization is not needed for this variable. Also marking this variable as volatile ensures the
most up todate value is always used.

Non-Runnable Thread States

In order for the thread to respond to the stop flag the thread has to be running. A thread is in a non-runnable state if

*

Its sleep method is invoked.

*

The thread calls the wait method to wait for a specific condition to be satisfied.

*

The thread is blocking on I / O.

If thread is in a non-runnable state, setting the stop flag variable will have no effect. In the above example, after calling the stop () method above, you have to wait until the sleep interval
has expired before the thread stops. To get the thread to stop promptly you need to break out of the sleep using interrupt (). (Interrupt () will also break out a wait method.)

public void stop () {

Thread tmpBlinker = blinker;

blinker = null;

if (tmpBlinker! = null) {

tmpBlinker.interrupt ();

}

}

Note: tmpBlinker does not need to be declared volatile because the load from volatile blinker is guaranteed to be atomic.

To summarize, now that the Thread stop () method has been found to be unsafe and has been depreciated, Sun suggests its functionality be replaced with a stop variable. However this by itself
is not sufficient to stop the thread promptly. You also need make sure that the thread runnable. At the very least this requires a call to the Thread interrupt () method to break out of sleep () and wait () methods that the thread may be trapped in, in some
lower level method. Breaking out of the third non-runnable state, blocking on I / O is more involved and will be discussed below.

IfInterruptedStop ()

In the ThreadReturn package I combined the need for a stop variable and the need to call interrupt () into one. As discussed above the only safe way to stop a thread is to exit its run ()
method. In order to do that the thread needs to be running, so when trying to stop a thread you would normally call its interrupt () method as part of the stopping process.

Now when a thread is in a sleep () or wait () method, calling interrupt () on that thread breaks out of the sleep or wait and throws an InterruptedException. Ensuring this exception propagates
all the way back up to the exit of the run () method is the simplest means of stopping a thread. Since the run () method is not declared to throw any checked exceptions, the InterruptedException needs to be caught and wrapped in an un-checked exception (such
as a RuntimeException). Java will insist you catch and handle the InterruptedException because it is a checked exception. So code your handler like so

try {

....

wait ();

} Catch (InterruptedException iex) {

throw new RuntimeException ("Interrupted", iex);

}

You may like to define your own un-checked exception class to use for this purpose so you can distinguish it from other RuntimeExceptions. In Java V1.5 there is a java.util.concurrent.CancellationException
you can use, although it does not have a constructor that takes a throwable which limits is usefulness. You are probably better off defining your own.

Then provided you do not catch the un-checked exception at some intermediate level, it will ensure the thread stops by exiting the run () method. There may be some cases where you want to
interrupt a thread‘s wait () or sleep () method when you don‘t want to stop the thread. In these cases you should put a try / catch block around the wait () or sleep () and catch and handle the InterruptedException there without propagating an exception back
up to the top run () method.

Now if the thread has started, and was not in a sleep () or wait () method, then calling interrupt () on it does not throw an exception. Instead it sets the thread‘s interrupted flag. This
flag can then be used as the thread‘s stop flag. To do this insert the following code fragment in those places you want to check for stopping the thread. (See the note below about stopping threads that have not yet started.)

Thread.yield (); / / let another thread have some time perhaps to stop this one.

if (Thread.currentThread (). isInterrupted ()) {

throw new InterruptedException ("Stopped by ifInterruptedStop ()");

}

The call isInterrupted () does not clear the thread‘s interrupted flag. You should avoid using the interrupted () method (not to be confused with interrupt ()) because a call to interrupted
() clears the thread‘s interrupted flag and will prevent you detecting the request to stop.

The static methods ThreadReturn.ifInterruptedStop (), FutureTalker.ifInterrruptedStop () and TaskUtilities.ifInterruptedStop () all contain this code. FutureTalker requires Java 1.5 while
the other two only need Java 1.4 or higher. So you can inserting the following line at strategic points in the thread in place of the previous code

TaskUtilities.ifInterrruptedStop ();

Why interrupt () is the answer.

In the preceding section I have shown how you can stop your thread by only using interrupt (). The question to be answered now is why this is the preferred method of stopping threads. The
answer lies in the code of the new Java V1.5 standard library java.util.concurrent. This standard library provides a unified means of passing Tasks to threads and retrieving the results, or the error if one occurred. This standard library, together with my
FutureTalker package provides Java V1.5 with the functionality my ThreadReturn package provided for Java V1.4.

Multi-Core Hardware

Before delving into the code, lets examine what java.util.concurrent means for multi-threaded programming. In java.util.concurrent Threads are abstracted to Tasks. When using Threads directly,
you override the run () method to do something useful and then create the Thread object and call start () which initializes the thread and calls the run () method. This thread initialization is not cheep. Allen Holub (Taming Java Threads, pp209) reports it
takes 15 times longer to start the thread then it does to create it. Also the number of threads can be a limited resource in some systems.

With this in mind you can see the advantage of separating the task to be run from the thread running it and using a pool of reusable threads to run the tasks. This functionality is provided
by java.util.concurrent. Instead on concentrating on Threads and their run () methods, java.util.concurrent instead talks about tasks which implement Callable <V>. The Callable <V> interface defines just one method,

V call () throws Exception

which computes a result, or throws an exception if unable to do so. This is a noticeable improvement over the Thread‘s run () method because call () can return a result and throw a checked
exception. Building on the Callable interface, java.util . concurrent provides classes for the asynchronous execution of Callable tasks. The Future <V> interface represents the result of an asynchronous task. FutureTalker is a concrete implementation of this
interface which provides the ability to add listeners which will be informed when the task terminates.

In a recent Scientific American article, "A Split at the Core" (Nov. 2004, Vol 291, No 5), Gibbs reported the move to multi-core processors which provides multiple processors in a single
package. To take advantage of such hardware , programs will need to run as many tasks in parallel as possible. This is where java.util.concurrent and the Callable interface come into their own. By separating the task from the thread that runs it and by reducing
the overhead of starting multiple tasks , the programmer can make many more operations into tasks and even break a task in to sub-tasks, leaving the thread pool factory class as the single point of configuration to efficiently allocate these tasks to utilize
the available processing power. Apart from the fact that java.util.concurrent is now a standard Java library, the move to multi-processor hardware should be enough to convince you that programming tasks based on the Callable and Future interfaces is the way
to go.

Cancelling a FutureTask

Getting back to why interrupt () is the answer, the Future interface provides a method to cancel a pending or running task;

boolean cancel (boolean mayInterruptIfRunning);

The method returns false if the task could not be cancelled, typically because it has already completed. As the argument suggests, you have a choice if the task has already started. You
can let if run to completion but ignore the result (mayInterruptIfRunning = false ) or you can try and interrupt it. Not surprisingly delving down into the code of the only concrete implementation of the Future interface provided by java.util.concurrent, FutureTask,
you find the code

if (mayInterruptIfRunning) {

Thread r = runner;

if (r! = null)

r.interrupt ();

}

}

As you can see the default method of stopping a task in java.util.concurrent is to call interrupt (). This is why using interrupt () as described above, is now the preferable way stop threads.
Java.util.concurrent takes care of clearing the thread‘s interrupted flag before it re-uses it.

Blocking I / O

As mentioned above threads are in the non-running state if they in a sleep () or wait () method or are blocking on I / O. Most read () methods block if data is not available and prior to
Java V1.4 there was no means interrupting a blocking read (). However Java V1.4 introduced the InterruptibleChannel interface. Classes implementing the InterruptibleChannel interface can interrupt blocking I / O. This will cause the channel to be closed, the
blocked thread to receive a ClosedByInterruptException, and the blocked thread‘s interrupt status to be set.

Lets look as a simple example of using an interruptible channel to overcoming a common blocking I / O problem. That of interrupting a thread that is blocked waiting for input from System.in.
To do this we create an InputStreamReader that is based on an interruptible channel.

new InputStreamReader (

Channels.newInputStream (

(New FileInputStream (FileDescriptor.in)). GetChannel ())));

The following code is a simple example of its use. (Note: this program needs to be run from the command line. It does not work when run from within Eclipse)

import java.io.BufferedReader;

import java.io.FileDescriptor;

import java.io.FileInputStream;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.nio.channels.Channels;

public class InterruptInput {

static BufferedReader in = new BufferedReader (

new InputStreamReader (

Channels.newInputStream (

(New FileInputStream (FileDescriptor.in)). GetChannel ())));

public static void main (String args []) {

try {

System.out.println ("Enter lines of input (user ctrl + Z Enter to terminate ):");

System.out.println ("(Input thread will be interrupted in 10 sec .)");

/ / Interrupt input in 10 sec

(New TimeOut ()). Start ();

String line = null;

while ((line = in.readLine ())! = null) {

System.out.println ("Read line: ‘" + line +"‘");

}

} Catch (Exception ex) {

System.out.println (ex.toString ()); / / printStackTrace ();

}

}

public static class TimeOut extends Thread {

int sleepTime = 10000;

Thread threadToInterrupt = null;

public TimeOut () {

/ / Interrupt thread that creates this TimeOut.

threadToInterrupt = Thread.currentThread ();

setDaemon (true);

}

public void run () {

try {

sleep (10000); / / wait 10 sec

} Catch (InterruptedException ex) {/ * ignore * /}

threadToInterrupt.interrupt ();

}

}

}

Classes that implement InterruptibleChannel include, FileChannel, ServerSocketChannel, SocketChannel, Pipe.SinkChannel and Pipe.SourceChannel, so in principle you can interrupt I / O for
files, sockets and pipes. The class TaskUtilities, which only requires Java 1.4 or higher, provides a number of static methods to create interruptible I / O for files.

Unfortunately there are still some problems with Sun‘s implementation of deriving Streams from Channels. These include

1.

flush does not flush

2.

you cannot interrupt a write to a file.

The problem with flush () is that when deriving an OutputStream from a channel, Sun has not linked the OutputStream‘s flush () method back to its underlying channel‘s force (true) method.
This means if you interrupt a file being written to, the file will be closed but you may not get the previous data even if you have called flush (). This is mainly a problem for log files and for debugging.

The second problem is that when writing to a file, interrupt () will not interrupt a write in progress. This is technically correct as the I / O is not blocking but actually writing. However
it means there is no way to interrupt writing a large block of data to a slow device. It would be useful if Sun modified the library code to allow writes to be interrupted. As it is you should avoid writing large blocks of data if you want the task to be responsive
to interrupts. Instead write a number of small blocks in a loop. Then after you call interrupt (), at the next write () the InterruptibleChannel will be closed and a ClosedByInterruptException will be thrown. As an alternative, given the problems with flush
() noted above, you may prefer not to use an interruptible file output, but instead write in small blocks and call TaksUtilities.ifInterruptedStop () between each write which will throw an InterruptedException if the thread has been interrupted.

Stopping Threads that have Not been Started

In this article I have suggested using the Thread‘s interrupted state as a flag to stop it, however in spite of what the Java docs on interrupt () say, a thread‘s interrupt state will not
be set if it has not yet been started (by calling start ()). Since each thread is an object is seems reasonable that its interrupted state be an internal field that can be set once the object has been created. Alas this is not the case in Java V1.5 and below.
This means special handling is required to stop a thread between the time it is created and time it is started. If you are using java.util.concurrent Tasks this is handled for you automatically when you use cancel () on a FutureTask. The ThreadReturn package
also handles this case via its ThreadReturn.stop (thread) method.

If you are writing your own small thread then you should follow the following example code.

private volatile Thread myThread;

public void stopMyThread () {

Thread tmpThread = myThread;

myThread = null;

if (tmpThread! = null) {

tmpThread.interrupt ();

}

}

public void run () {

if (myThread == null) {

return; / / stopped before started.

}

try {

/ / All the run () method‘s code goes here

...

/ / Do some work

Thread.yield (); / / let another thread have some time perhaps to stop this one.

if (Thread.currentThread (). isInterrupted ()) {

throw new InterruptedException ("Stopped by ifInterruptedStop ()");

}

/ / Do some more work

...

} Catch (Throwable t) {

/ / Log / handle all errors here

}

}

However most threads need to return a result or at least notify the caller if they fail, for these reasons you are better off using java.util.concurrent, FutureTalker or ThreadReturn even
for small threads.

Summary

This article has covered some of the history of stopping Java threads and shown how both a stop flag and a call to interrupt () is required for prompt termination of threads. With the introduction
of Java V1.5 java.util.concurrent expects interrupt ( ) alone to stop the thread. To make this effective it was suggested the the thread‘s interrupt flag be used as the stop flag and an ifInterruptedStop () method be used to check for the interrupt flag being
set and to throw an exception. Stopping the thread depends on two other things, i) that the interrupt exception thrown propagates back up and out of the run method (or call method) and ii) that the thread is in a running state. The problem of Blocking I /
O was discussed and a solution based on InterruptibleChannels presented. The existing problems in the Sun library implementation where discussed.

时间: 2024-11-05 19:28:38

How to stop a Thread(怎样停止一个线程)的相关文章

Java并发(基础知识)—— 创建、运行以及停止一个线程

0.介绍 在计算机世界,当人们谈到并发时,它的意思是一系列的任务在计算机中同时执行.如果计算机有多个处理器或者多核处理器,那么这个同时性是真实发生的:如果计算机只有一个核心处理器那么就只是表面现象. 现代所有的操作系统都允许并发地执行任务.你可以在听音乐和浏览网页新闻的同时阅读邮件,我们说这种并发是进程级别的并发.而且在同一进程内,也会同时有多种任务,这些在同一进程内运行的并发任务称之为线程. 在这里我们要讨论的是线程级并发.Java提供了Thread类,使我们能够在一个Java进程中运行多个线

java停止一个线程

Thread类中有start(), stop()方法,不过stop方法已经被废弃掉. 平时其实也有用过,共享一个变量,相当于标志,不断检查标志,判断是否退出线程 如果有阻塞,需要使用Thread的interrupt()方中断阻塞,线程开始检查标志(PS:抛出异常不会退出循环) ------------------------------------------------------------我是copy分割线------------------------------------------

java如何正确停止一个线程

Thread类中有start(), stop()方法,不过stop方法已经被废弃掉. 平时其实也有用过,共享一个变量,相当于标志,不断检查标志,判断是否退出线程 如果有阻塞,需要使用Thread的interrupt()方中断阻塞,线程开始检查标志(PS:抛出异常不会退出循环) ------------------------------------------------------------我是copy分割线------------------------------------------

如何停止一个正在运行的java线程

与此问题相关的内容主要涉及三部分:已废弃的Thread.stop().迷惑的thread.interrupt系列.最佳实践Shared Variable. 已废弃的Thread.stop() @Deprecated public final void stop() { stop(new ThreadDeath()); } 如上是Hotspot JDK 7中的java.lang.Thread.stop()的代码,学习一下它的doc: 该方法天生是不安全的.使用thread.stop()停止一个线程

java 加入一个线程、创建有响应的用户界面 。 示例代码

javajava 加入一个线程.创建有响应的用户界面 . 示例代码 来自thinking in java 4 21章  部分代码  目录21.2.11 thinking in java 4免费下载:http://download.csdn.net/detail/liangrui1988/7580155 package org.rui.thread.concurrent; /** * 加入一个线程 * 一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行

创建一个实例&amp;创建一个线程。。

1 using System; 2 using System.Threading; 3 4 5 namespace WorkerThread02 6 { 7 class ThreadTest 8 { 9 bool done; 10 static void Main() 11 { 12 ThreadTest tt=new ThreadTest();//创建一个类的实例 13 Thread t=new Thread(WriteY);//创建一个线程. 14 .............. 15 ...

Java再学习——停止一个正在运行的线程

关于这个问题,先了解一下Thread类方法中被废弃的那些方法.suspend(), resume(),stop()/stop(Throwable obj),destroy() 首先,stop(Throwable obj)和destroy()方法在最新的Java中直接就不支持了,没必要去看了.我们只需瞧瞧suspend(), resume(), stop()这三个就行了; suspend()——让当前线程暂停执行 resume()——让当前线程恢复执行 当调用suspend()的时候,线程并没有释

java如何停止一个运行的线程?

关于线程的一点心得 //首先导入需要的包 improt java.util.Timer;import java.io.File;import java.util.TimerTask; //首先需要创建一个线程并且一直运行,然后创建一个计时器任务来触发事件(比如创建一个stop.txt文件,如果发现文件存在则停止运行程序). public class Stop{ public static void main(String args[]){ //创建线程,运行. Count cou=new Cou

ZeroMQ接口函数之 :zmq_ctx_shutdown - 停止一个ZMQ context

ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_ctx_shutdown zmq_ctx_shutdown(3) ØMQ Manual - ØMQ/4.1.0 Name zmq_ctx_shutdown - 停止一个ZMQ context Synopsis int zmq_ctx_shutdown (void *context); Description zmq_ctx_shutdown() 函数会停止ZMQ 环境上下文context. 停止context会