- 浏览: 131550 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
qq466862016:
不错的文章
JDK动态代理与CGLIB代理的对比 -
jinxiongyi:
你好,jpedal pdf转换图片的 画质,怎么提高。。我转 ...
介绍几款PDF转图片的开源工具 -
qqdwll:
转图片消耗的内存还是不小。 有时间得找找有没有更好的办法, 把 ...
介绍几款PDF转图片的开源工具 -
xiaoyao3857:
Thanks for your work!It's help ...
Keeping Eclipse running clean (转载) -
iceside:
图片讲解非常详细,说清了引用复制是怎么回事
Java 值传递的终极解释
source file http://forward.com.au/javaProgramming/HowToStopAThread.html
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.
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.
发表评论
-
介绍几款PDF转图片的开源工具
2011-09-09 00:40 4491最近项目中有个需求需要把PDF转成一张图。经过调查,有 ... -
jadclipse(反编译Eclipse插件)
2011-07-19 19:13 1619Jad Java decompiler plugin for ... -
Java开发时候的内存溢出
2011-07-13 17:33 1158这里以tomcat环境为例, ... -
class loader
2011-07-08 17:23 0Because Class.getResource() eve ... -
Jakarta-Common-BeanUtils使用笔记
2011-07-06 16:55 1302原文转发http://blog.csdn.net/fa ... -
基于MVC模式Struts框架研究
2011-04-13 20:02 1299不做web开发多年了, 可偶尔去面试的时候, 还是 ... -
Java反射与动态代理
2011-04-13 15:08 938这篇文章是 成富 先生在InfoQ上Java 深度历险系列的一 ... -
Java枚举类型
2011-04-04 19:50 753Tiger中的一个重要新特性是枚举构造,它是一种新的Java枚 ... -
Java 值传递的终极解释
2011-03-21 22:49 1940对于Java的值传递, 你真的了解么? Ja ... -
六种异常处理的陋习
2011-03-20 03:21 731你觉得自己是一个Java专 ... -
数组初始化
2011-03-20 02:40 834数组初始化,你觉得简单吗? a.如果你觉得简单,那请看下面的 ... -
Java 实现 hashCode 方法
2011-03-11 17:07 1131原文 http://www.javapractices.com ... -
Java 中 immutable class 以及怎样实现immutable 类
2011-03-11 16:47 1330原文 http://www.javapractices.com ... -
Java 内部类介绍
2011-02-16 17:14 948转载: http://zhidao.baidu.com/que ... -
Java 中的Clone 学习总结
2011-01-25 18:22 26941. 一个类需要实现clone. 一个最佳实践是它需要实现 C ... -
java 通过流, nio 移动文件或者文件夹
2011-01-04 17:54 1814我们用例子说明java怎样通过不同的方式移动文件或文件夹。 ... -
转 深入探讨SOAP、RPC和RMI
2010-12-17 00:34 1009这篇文章是从网上转下来的。 原文应该是写于2001年。 10 ... -
java 6 中的性能优化
2010-12-07 15:30 1407文章转载自: http://www ... -
创建强健,稳定的 JMS 系统
2010-12-07 15:21 949The most reliable way to produc ... -
Java Modifier Summary
2010-11-12 15:10 843<tbody> <tr> ...
相关推荐
斑马打印机 ZT411 ZT421中文手册
ZT213LEEA,国外找到的数据手册。
ZT410打印机IP地址设置网络打印机ZT410打印机IP地址设置网络打印机ZT410打印机IP地址设置网络打印机ZT410打印机IP地址设置网络打印机ZT410打印机IP地址设置网络打印机
斑马打印机ZT210用户指南
这款RS485通信芯片zt13085e用的不多,网上很少这款的原理图库和PCB库,自己画了一个。
赠送jar包:zt-exec-1.9.jar; 赠送原API文档:zt-exec-1.9-javadoc.jar; 赠送源代码:zt-exec-1.9-sources.jar; 赠送Maven依赖信息文件:zt-exec-1.9.pom; 包含翻译后的API文档:zt-exec-1.9-javadoc-API文档-...
斑马zt410驱动是由斑马官方推出的打印机驱动程序,如果你的打印机与电脑的连接出现了异常而导致打印机无法正常的使用,下载此驱动能帮你很好的解决这个问题,欢迎购买了此型号打印机的朋友下载使用!斑马zt410打印机...
赠送jar包:zt-exec-1.9.jar; 赠送原API文档:zt-exec-1.9-javadoc.jar; 赠送源代码:zt-exec-1.9-sources.jar; 赠送Maven依赖信息文件:zt-exec-1.9.pom; 包含翻译后的API文档:zt-exec-1.9-javadoc-API文档-...
斑马zt410中文库
ZT213/ZT213LEEA USB多通道收发/驱动芯片规格书V2.10及手册,内含ZT213规格参数说明,选型参考表以及应用原理图。更多USB相关应用及方案可查看作者文章了解....
霍尼维尔 ZT930 点火变压器PDF,霍尼维尔 ZT930 点火变压器
斑马ZT510打印机驱动文件
ZT2835W0M1贴片发光二极管规格书pdf,ZT2835W0M1贴片发光二极管规格书
ZT短信平台接口开发文档
Linux 下编程的三个重要工具(ZT).rar
结合窑街海石湾煤矿6114综放工作面的情况,详细介绍了ZT18000/22/40型一主两副式端头液压支架在工作面的应用情况、支架结构特点和使用效果分析,对煤矿中相似工况下端头支架选型具有一定参考意义。
本驱动程序适用斑马Zebra ZT210-220-230系列工业条码打印机,附打印机快速参考指南
斑马zt210是一款专为中国市场设计的工业条码打印机,非常适合不需要频繁更换标签的条码标签应用。这里给大家提供斑马zt210驱动下载,推荐有需要的用户下载安装。斑马zt210打印机优势:◆ 节省空间* 小巧紧凑和流线型...
ZT210 230加载介质和碳带,很详细的视频资料 欢迎下载哦
单片机多级菜单编程实现(ZT) 建立一个树状的菜单结构,用链表实现