`
qqdwll
  • 浏览: 136674 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

[ZT] 怎样停止一个线程或者任务

    博客分类:
  • 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.
分享到:
评论

相关推荐

    ZT213/ZT213LEEA规格书V2.10-低功耗RS232多通道USB收发器/驱动器芯片手册

    ZT213/ZT213LEEA是一款专为低功耗应用设计的RS232多通道USB收发器和驱动器芯片,广泛应用于数据通信、工业控制、物联网设备等领域。本文将深入探讨其规格参数、功能特点、选型指南及应用实例,帮助读者全面了解这款...

    zt411-zt421-ug-zhcn_ZT411/ZT421_斑马打印机手册_

    这份中文手册详细地介绍了ZT411和ZT421的操作、维护以及常见问题的解决方法,对于用户来说是一份非常重要的参考资料。 一、打印机概述 ZT411和ZT421是斑马技术公司推出的一系列高级热转印桌面打印机,具备高速打印...

    ZT213LEEA.PDF

    **ZT213LEEA** 是一款由 Zywyn Corporation 生产的低功耗 RS232 通信接口芯片。该系列器件采用 +5V 供电,支持 EIA/TIA-232 和 V.28/V.24 通信标准,并具有较低的功耗需求。这些收发器最多可包含五个线路驱动器和五...

    Zebra ZT230 条码打印机驱动

    斑马(Zebra)ZT230条码打印机是一款广泛应用在工业环境中的高效设备,其驱动程序是确保打印机正常工作的重要组成部分。本驱动程序专为Zebra的ZT210、ZT220及ZT230系列打印机设计,提供了全面的功能支持,以实现高...

    斑马zt410中文库

    斑马(Zebra)ZT410是一款先进的桌面级条码打印机,被广泛应用于物流、零售、医疗、制造业等多个行业。这款打印机以其高效、耐用和易于操作的特点深受用户喜爱。"斑马zt410中文库"指的是为ZT410打印机特别设计的中文...

    ZT410打印机IP地址设置网络打印机

    ZT410打印机IP地址设置网络打印机ZT410打印机IP地址设置网络打印机ZT410打印机IP地址设置网络打印机ZT410打印机IP地址设置网络打印机ZT410打印机IP地址设置网络打印机

    斑马打印机ZT210用户指南

    本用户指南旨在为Zebra ZT210/ZT220/ZT230打印机的用户提供操作和维护指南,该设备是一种工业级的条码打印机,具有高速打印、高速处理和高质量打印输出等特点。 版权信息 本手册的版权和这里描述的打印机软件和/或...

    ZT7548 Datasheet Rev.1.0.3.pdf

    ZT7548是一款第五代电容式触控屏幕控制器,支持30x18或18x30的通道配置,可以同时检测最多10个触点。该控制器能够与最多8个键与TSP(触控屏面板)或FPC(柔性印刷电路板)模式一起工作,在多点触控时无扫描率下降的...

    RS485通信芯片zt13085e的原理图库和PCB库

    4. **正确设置使能引脚**:RS485芯片通常有一个使能引脚,用于切换芯片的接收和发送状态,确保在不使用时关闭发射器,以防止信号冲突。 总的来说,ZT13085E作为一款RS485通信芯片,是构建工业通信网络的重要组成...

    斑马打印机(ZT210).docx

    斑马打印机ZT210是一款专业的工业级条形码和标签打印机,广泛应用于物流、零售、医疗等行业的标签制作。以下是对如何设置和使用斑马ZT210打印机的详细步骤: 首先,我们需要安装电脑驱动。双击下载好的驱动程序文件...

    斑马ZT510打印机驱动文件

    斑马ZT510打印机驱动文件

    证通ZT598金属键盘开发资料.rar

    总之,证通ZT598金属键盘的开发工作是一项技术性强、要求细致的任务。开发者需要具备一定的嵌入式系统开发经验,对SDK的深入理解和熟练应用是成功集成的关键。通过学习和实践,开发者将能充分利用这款设备的安全特性...

    斑马zt210打印机驱动 v5.1.07.5146 官方版

    斑马zt210是一款专为中国市场设计的工业条码打印机,非常适合不需要频繁更换标签的条码标签应用。这里给大家提供斑马zt210驱动下载,推荐有需要的用户下载安装。斑马zt210打印机优势:◆ 节省空间* 小巧紧凑和流线型...

    zt.zip_判断

    在IT行业中,"zt.zip_判断"这个标题可能是指一个压缩文件,其中包含了用于执行某种判断逻辑的程序或数据。这个文件"zt.h"可能是C++、C或类似编程语言中的头文件,它可能包含了函数声明、常量定义或者枚举类型等,...

    zt-exec-1.9-API文档-中文版.zip

    赠送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文档-...

    ZT598命令集技术手册(C45)(B46.04)_证通密码键盘指令集_

    《ZT598命令集技术手册》是针对证通密码键盘操作与通信的重要参考资料,主要涵盖了一系列串口指令的详细说明。这份手册对于理解和掌握如何通过串行接口与密码键盘进行有效通信至关重要,尤其在金融、安防以及其他...

    项目管理工具——ZT

    1. **需求管理**:禅道提供了一个完整的需求管理平台,团队可以在这里记录、讨论、评审和跟踪需求变化,确保产品方向与市场需求保持一致。需求可分解为子需求,便于细化工作,并支持需求关联用例,确保需求的可测试...

    zebra ZT400系列打印机技术手册

    ### zebra ZT400系列打印机技术手册 #### 知识点概述: 1. **版权及法律声明**:Zebra ZT400系列打印机技术手册的版权及相关软件固件的所有权归属ZIH Corp.及其许可证持有者,未经授权复制会受到法律制裁。 2. **...

    斑马zt410打印机驱动 v5.1.7 官方最新版

    斑马zt410驱动是由斑马官方推出的打印机驱动程序,如果你的打印机与电脑的连接出现了异常而导致打印机无法正常的使用,下载此驱动能帮你很好的解决这个问题,欢迎购买了此型号打印机的朋友下载使用!斑马zt410打印机...

    ZT210 230加载介质和碳带

    《ZT210 230加载介质和碳带》 在条形码和标签打印领域,Zebra公司的ZT210和ZT230打印机是广泛应用的设备,以其可靠性和效率赢得了广大用户的青睐。这两个型号的打印机都属于入门级工业级条码打印机,适用于各种商业...

Global site tag (gtag.js) - Google Analytics