Java如何停止執行緒,確定你知道的都是正確的麼?

weixin_34124651發表於2018-03-25

回想當年剛開始做Java開發的時候,如何停止執行緒執行可是難倒了一批人,停止執行緒的方法眾說紛紜:

  1. 呼叫Thread.stop() :
Thread thread = new Thread(){
    @Override
    public void run() {
        for (int i=0; i < 999 && !isCanceled; i++) {
            System.out.println("哈哈,我還沒死...");
        }
    }
};
thread.start();

// kill you now
thread.stop();

點評: 不用多說,直接拉出去斬了

  1. 執行緒外面定義boolean變數標識為是否已經取消,執行緒裡每個迴圈地方加判斷,變數變成了取消則停止迴圈:
private volatile boolean isCanceled = false;

new Thread(){
    @Override
    public void run() {
        for (int i=0; i < 999 && !isCanceled; i++) {
            System.out.println("哈哈,我還沒死...");
        }
    }
}.start();

// kill thread
isCanceled = true;

點評: 太片面,執行緒裡Thread.sleep(999999)取消不了的

  1. 通過呼叫interrupt() :
Thread thread = new Thread(){
    @Override
    public void run() {
        sleep(99999);
        System.out.println("剛睡醒,我還沒死!!!");
    }
};
thread.start();

// kill you now
thread.interrupt();

點評:也是太片面,for迴圈取消不了

以上方案意思是有那麼一點了,只是零零碎碎,如何將兩個方案結合是今天的第一個話題。
在Thread內提供了一個方法叫interrupted(), 其返回值即告知是否執行緒被interrupt()執行過,簡單說:無需自己定義boolean變數,可以用它代替,如果執行緒能被interrupt()而停止正好,如果是內部迴圈操作可以用它作為迴圈停止的條件:

Thread thread = new Thread(){
    @Override
    public void run() {
        for (int i=0; i < 999 && !Thread.interrupted(); i++) {
            System.out.println("哈哈,我還沒死...");
        }
    }
};

其實,還有一種情況是停止不了的,類似socket/http通訊這種不能被interrputted的情況,當然也有巧妙的辦法:過載Thread的interrput()方法:

// 過載interrupt 通過關閉socket起到關閉執行緒的作用
@Override
public void interrupt() {
    try {
        socket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

今天的第二個話題是,使用單個執行緒的情況在專案中極少,很多情況使用執行緒池託管的,那麼執行緒池裡如何停止某個task呢?
JDK5提供了ExecuteService,並通過Executors提供了多個候選的執行緒池選擇方案,ExecutorService提交task有兩種方式:execute()和submit(), 我們這裡對submit()比較感興趣,因為submit()會返回Feature,不管submit的是 Runnable還是Callable都會有Feature返回,Feature物件提供了一系列API,如:

  1. cancel(boolean mayInterruptIfRunning) ;
  2. isCanceled();
  3. get();
  4. get(long timeout, TimeUnit unit);
  5. isDone()

看到第一個API,很是讓人興奮,難倒這真的可以一步到位?但很不好意思,依然不行,其實跟第一個話題的原理一摸一樣,如果內部有阻塞執行緒的操作會因此而退出,如: wait(), join(), sleep() 等,但是遇到IO阻塞操作,比如進行socket通訊,僅僅通過呼叫cancel(true)是不行的,不過可以過載interrupt() 關閉socket即可, 遇到大量迴圈也要通過Thread.interrupted()作為迴圈結束的條件:

class Task extends Thread{
    private Socket socket;

    public Task(){
        // init socket
    }

    // 過載interrupt 通過關閉socket起到關閉執行緒的作用
    @Override
    public void interrupt() {
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public void func () {
    ExecutorService timer = Executors.newCachedThreadPool();

    Future<?> future = timer.submit(new Task());
    future.cancel(true);

}

好了,夜深了...回憶到此結束

相關文章