韩后茶蕊嫩白系列:Interrupting Java threads | TechRepublic

来源:百度文库 编辑:九乡新闻网 时间:2024/04/27 22:57:00
Writing multithreaded programs in Java, with its built-in support forthreads, is fairly straightforward. However, multithreading presents awhole set of new challenges to the programmer that, if not correctlyaddressed, can lead to unexpected behavior and subtle, hard-to-finderrors. In this article, we address one of those challenges: how tointerrupt a running thread.

Background
Interruptinga thread means stopping what it is doing before it has completed itstask, effectively aborting its current operation. Whether the threaddies, waits for new tasks, or goes on to the next step depends on theapplication.

Although it may seem simple at first, you must takesome precautions in order to achieve the desired result. There are somecaveats you must be aware of as well.

First of all, forget the Thread.stop method. Although it indeed stops a running thread, the method is unsafe and was deprecated, which means it may not be available in future versions of the Java.

Another method that can be confusing for the unadvised is Thread.interrupt. Despite what its name may imply, the method does not interrupt a running thread (more on this later), as Listing A demonstrates. It creates a thread and tries to stop it using Thread.interrupt. The calls to Thread.sleep() give plenty of time for the thread initialization and termination. The thread itself does not do anything useful.

If you run the code in Listing A, you should see something like this on your console:
Starting thread...
Thread is running...
Thread is running...
Thread is running...
Interrupting thread...
Thread is running...
Thread is running...
Thread is running...
Stopping application...

Even after Thread.interrupt() is called, the thread continues to run for a while.

Really interrupting a thread
Thebest, recommended way to interrupt a thread is to use a shared variableto signal that it must stop what it is doing. The thread must check thevariable periodically, especially during lengthy operations, andterminate its task in an orderly manner. Listing B demonstrates this technique.

Running the code in Listing B will generate output like this (notice how the thread exits in an orderly fashion):
Starting thread...
Thread is running...
Thread is running...
Thread is running...
Asking thread to stop...
Thread exiting under request...
Stopping application...

Althoughthis method requires some coding, it is not difficult to implement andgive the thread the opportunity to do any cleanup needed, which is anabsolute requirement for any multithreaded application. Just be sure todeclare the shared variable as volatile or enclose any access to it into synchronized blocks/methods.

Sofar, so good! But what happens if the thread is blocked waiting forsome event? Of course, if the thread is blocked, it can't check theshared variable and can't stop. There are plenty of situations when thatmay occur, such as calling Object.wait(), ServerSocket.accept(), and DatagramSocket.receive(), to name a few.

Theyall can block the thread forever. Even if a timeout is employed, it maynot be feasible or desirable to wait until the timeout expires, so amechanism to prematurely exit the blocked state must be used.

Unfortunatelythere is no such mechanism that works for all cases, but the particulartechnique to use depends on each situation. In the following sections,I'll give solutions for the most common cases.

Interrupting a thread with Thread.interrupt()
As demonstrated in Listing A, the method Thread.interrupt()does not interrupt a running thread. What the method actually does isto throw an interrupt if the thread is blocked, so that it exits theblocked state. More precisely, if the thread is blocked at one of themethods Object.wait, Thread.join, or Thread.sleep, it receives an InterruptedException, thus terminating the blocking method prematurely.

So,if a thread blocks in one of the aforementioned methods, the correctway to stop it is to set the shared variable and then call the interrupt() method on it (notice that it is important to set the variable first). If the thread is not blocked, calling interrupt()will not hurt; otherwise, the thread will get an exception (the threadmust be prepared to handle this condition) and escape the blocked state.In either case, eventually the thread will test the shared variable andstop. Listing C is a simple example that demonstrates this technique.

As soon as Thread.interrupt()is called in Listing C, the thread gets an exception so that it escapesthe blocked state and determines that it should stop. Running this codeproduces output like this:
Starting thread...
Thread running...
Thread running...
Thread running...
Asking thread to stop...
Thread interrupted...
Thread exiting under request...
Stopping application...

Interrupting an I/O operation
Butwhat happens if the thread is blocked on an I/O operation? I/O canblock a thread for a considerable amount of time, particularly ifnetwork communication is involved. For example, a server may be waitingfor a request, or a network application may be waiting for an answerfrom a remote host.

If you're using channels, available with the new I/O API introduced in Java 1.4, the blocked thread will get a ClosedByInterruptException exception. If that is the case, the logic is the same as that used in the third example—only the exception is different.

Butyou might be using the traditional I/O available since Java 1.0, sincethe new I/O is so recent and requires more work. In this case, Thread.interrupt() doesn't help, since the thread will not exit the blocked state. Listing D demonstrates that behavior. Although the interrupt() method is called, the thread does not exit the blocked state.

Fortunately, the Java Platform provides a solution for that case by calling the close()method of the socket the thread is blocked in. In this case, if thethread is blocked in an I/O operation, the thread will get a SocketException exception, much like the interrupt() method causes an InterruptedException to be thrown.

The only caveat is that a reference to the socket must be available so that its close() method can be called. That means the socket object must also be shared. Listing E demonstrates this case. The logic is the same as in the examples presented so far.

And here's the sample output you can expect from running Listing E:
Starting thread...
Waiting for connection...
Asking thread to stop...
accept() failed or interrupted...
Thread exiting under request...
Stopping application...

Multithreadingis a powerful tool, but it presents its own set of challenges. One ofthese is how to interrupt a running thread. If properly implemented,these techniques make interrupting a thread no more difficult than usingthe built-in operations already provided by the Java Platform.