1. 程式人生 > >java多執行緒程式設計--如何終止一個執行緒

java多執行緒程式設計--如何終止一個執行緒

1. Thread.stop()函式

stop()函式終止執行緒就像是強行拔掉電源線關機一樣,可能會帶來未知風險,因此目前不再推薦使用這種方式。請忘記它吧~~

2. 改變標誌變數狀態

通常我們會線上程中使用一個標誌變數來控制執行緒的執行,如:

public class TestCallable implements Runnable {


    private boolean running = true;

    public void stop() {
        this.running = false;
    }

    public void run() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            while (running) {
                System.out.println("執行緒正在執行中...");
                Thread.sleep(20000);
            }
            System.out.println("執行緒被終止.");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        try {
            TestCallable callable = new TestCallable();
            Thread th = new Thread(callable);
            th.start();
            Thread.sleep(1000);
            callable.stop();
            System.out.println("已下達終止執行緒命令。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
執行上述程式碼就能發現,執行緒阻塞在reader.readLine()時,即使主執行緒改變了標誌變數,但是並不能立即結束子執行緒,只有等待阻塞被打破,且執行到下一次迴圈條件判斷的時候才能終止。所以在使用這種方法時,應該考慮到阻塞這種情況。當然,如果整個迴圈內的操作屬於同一事務時,這種方法倒很不錯。

3. 中斷函式interrupt()

網上有很多論調說,終止執行緒的正確處理方式是使用interrupt中斷,但真的是這樣嗎?實踐出真知,拭目以待吧! 如上2所述,如果執行緒中有Thread.sleep()阻塞時,改變標識變數無法達到終止執行緒的目的,那麼此時可以使用Thread類的中斷函式interrupt(); 如:
public class TestCallable extends Thread {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    public void stopThread() {
      interrupt();
    }

    public void run() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            reader.close();
            while (!isInterrupted()) {
                System.out.println("執行緒正在執行中...");
                Thread.sleep(20000);
            }
            System.out.println("執行緒被終止.");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            TestCallable cal = new TestCallable();
            cal.start();
            Thread.sleep(2000);
            cal.stopThread();
            System.out.println("已下達終止執行緒命令。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
呼叫執行緒物件的interrupt()時,sleep的執行緒會丟擲InterruptedException異常,從而中斷迴圈,終止執行緒。 但是如果是IO如輸入這些阻塞,中斷的方法又不起作用了,還有就是對於沒有阻塞的執行緒,呼叫interrupt()是達不到終止執行緒的效果的。 那麼,interrupt()中斷線上程的哪些阻塞操作中能起到丟擲InterruptedException作用呢? 我們來做一個實驗,程式碼如下:
public class TestCallable {

    public static void main(String[] args) {

        // 測試無阻塞的情況,進行中斷
        Thread thread1 = new Thread(){
            public void run(){
                try {
                    long time = System.currentTimeMillis();
                    while(System.currentTimeMillis()-time<5000){
                    }
                    System.out.println("A1");
                } catch(Exception e) {
                    System.out.println("B1");
                    System.out.println("B1 : " + e.toString());
                }
            }
        };
        thread1.start();
        thread1.interrupt();

        //線上程sleep狀態下進行中斷
        Thread thread2 = new Thread(){
            public void run(){
                try {
                    this.sleep(5000);
                    System.out.println("A2");
                } catch (Exception e) {
                    System.out.println("B2");
                    System.out.println("B2 : " + e.toString());
                }
            }

        };

        thread2.start();
        thread2.interrupt();

        //線上程wait狀態下進行中斷,其中wait()沒有在同步塊中
        Thread thread3 = new Thread(){
            public void run(){
                try {
                    this.wait(5000);
                    System.out.println("A3");
                } catch (Exception e) {
                    System.out.println("B3");
                    System.out.println("B3 : " + e.toString());
                }
            }

        };

        thread3.start();
        thread3.interrupt();

        //線上程wait狀態下進行中斷,其中wait()在同步塊中
        Thread thread4 = new Thread(){
            public void run(){
                try {
                    synchronized(this){
                        this.wait(5000);
                        System.out.println("A4");}
                } catch (Exception e) {
                    System.out.println("B4");
                    System.out.println("B4 : " + e.toString());
                }
            }
        };

        thread4.start();
        thread4.interrupt();

        // join阻塞時進行中斷
        Thread thread5 = new Thread() {
            public void run() {
                try {
                    this.join(5000);
                    System.out.println("A5");
                } catch (Exception e) {
                    System.out.println("B5");
                    System.out.println("B5 : " + e.toString());
                }
            }
        };
        thread5.start();
        thread5.interrupt();
    }
}
輸出結果:
B2
B4
B4 : java.lang.InterruptedException
B3
B3 : java.lang.IllegalMonitorStateException
B5
B2 : java.lang.InterruptedException: sleep interrupted
B5 : java.lang.InterruptedException
A1
結果分析: 輸出A1: 說明在無阻塞的時候,中斷沒有作用。 輸出B2 : java.lang.InterruptedException: sleep interrupted 說明sleep操作阻塞時,中斷生效。 輸出B3 : java.lang.IllegalMonitorStateException 非中斷引起的異常,這是wait()用法錯誤,這裡就不詳述。但正好可以看出,不論什麼異常,都可以中斷執行緒~ 輸出B4: java.lang.InterruptedException 說明wait操作阻塞時,中斷生效。 輸出B5:java.lang.InterruptedException 說明join操作阻塞時,中斷生效。 所以,針對執行緒的上述阻塞,都可以使用interrupted()方法中斷執行緒,應該說,interrupted是對處於WAITTING 和TIME_WAITTING狀態下的執行緒有用。 那麼問題來了,如果是其他阻塞情況呢?如IO阻塞,又該如何中斷阻塞?其實從上面的例子可以大致得出結論:中斷阻塞就可以。 如:
public class TestCallable implements Runnable {

    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    public void stop() {
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void run() {
        try {
            while (true) {
                System.out.println("執行緒開始執行...");
                String cmd = reader.readLine();
            }
        } catch (IOException e) {
            System.out.println("IO異常,執行緒結束。");
            e.printStackTrace();
        }


    }

    public static void main(String[] args) {
        TestCallable callable = new TestCallable();
        Thread th = new Thread(callable);
        th.start();
        callable.stop();

    }

}
通過關閉通道,丟擲一個異常來中斷阻塞,達到終止執行緒的目的。對於網路IO也是如此。

4. 總結

通過上述分析可知,要真正按我們的預期正確結束一個執行緒真的不容易! 不過總得說來,終止執行緒有兩類方法: 針對沒有阻塞的情況:設定標誌變數,讓執行緒正常自然死亡,和諧! 針對有阻塞的情況:中斷阻塞,靠丟擲異常終止執行緒,看似暴力,但如果是我們預期的異常,那也是安全的! 所以,要結合你自身程式的應用場景以及程式碼編寫的具體情況,來確定最終方案。 通過上述分析總結,我個人認為較好的方式是: (1)不管有沒有阻塞情況,都用標誌變數控制執行緒迴圈。          網上有人使用如下的方式來代替標誌變數,其實是不正確的,因為這樣就依賴於執行緒的中斷狀態,可能導致執行緒非預期終止。
public void run() {
    while(!Thread.currentThread().isInterrupted()) {
      ...
    }
}

(2)用一個函式封裝終止執行緒的操作,暴漏給外部呼叫。 (3)針對執行緒中出現的阻塞情況,使用相應的中斷方法,如執行緒的WATTING,TIME_WAITTING狀態,可以用interrupted()方法,對於IO阻塞,可以關閉IO通道等。
public class TestCallable implements Runnable {
    // 不管是不是阻塞情況,都用標誌變數控制執行緒迴圈
    // 需要宣告為validate,確保對所有執行緒的可見性。
    private validate boolean running;

    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    /**
     * 用一個函式封裝終止執行緒的操作
     */
    public void stop() {
        running = false;
        
        /*
         * 針對執行緒中出現的阻塞情況,使用相應的中斷方法。
         * 如執行緒的WAITTING,TIME_WAITTING狀態,可以用interrupted()
         * 對IO阻塞,可以關閉IO通道等。
         */
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void run() {
        try {
            running = true;
            while (running) {
                System.out.println("執行緒開始執行...");
                String cmd = reader.readLine();
            }
        } catch (IOException e) {
            System.out.println("IO異常,執行緒結束。");
            e.printStackTrace();
        }


    }

    public static void main(String[] args) {
        TestCallable callable = new TestCallable();
        Thread th = new Thread(callable);
        th.start();
        callable.stop();
    }

}


5. 碎碎念

本來只想總結一下終止執行緒,沒想到又扯出了執行緒狀態,這個就下回再詳解吧。真是點帶線,線帶面啊,真真是“知識的海洋”...... 不過,收穫不小,對於網路上得種種說法,進行試驗、排錯,最後得出自己的解法。如果您有更好的建議,熱烈歡迎!! 最近公司出推行新制度了,比較混亂的狀態,偷閒梳理下部落格,優哉遊哉~~

相關推薦

java執行程式設計--如何終止一個執行

1. Thread.stop()函式 stop()函式終止執行緒就像是強行拔掉電源線關機一樣,可能會帶來未知風險,因此目前不再推薦使用這種方式。請忘記它吧~~ 2. 改變標誌變數狀態 通常我們會線上程中使用一個標誌變數來控制執行緒的執行,如: public class T

Java執行程式設計核心技術 —— 執行間通訊

執行緒是作業系統中獨立的個體,但這些個體如果不經過特殊的處理就不能成為一個整體。執行緒間的通訊就是成為整體的比用方案之一,可以說,使執行緒間進行通訊後,系統之間的互動性會更強大,在大大提高CPU利用率的同時還會使程式設計師對個執行緒任務在處理的過程中進行有效的把控與監督。 1、

JAVA執行之當一個執行執行死迴圈時會影響另外一個執行嗎?

一,問題描述 假設有兩個執行緒在併發執行,一個執行緒執行的程式碼中含有一個死迴圈如:while(true)....當該執行緒在執行while(true)中程式碼時,另一個執行緒會有機會執行嗎? 二,示例程式碼(程式碼來源於網際網路) 1 public class Service { 2

python 執行程式設計一個經典例子)

python 多執行緒經典案例(摘自《python核心程式設計》) 使用佇列的資料結構,生產者生產商品,消費者選取商品,且時間均不固定 from random import randint from time import sleep from queu

關於java中怎麼終止一個執行執行

其實現在要對一個迴圈執行的執行緒關閉,對迴圈執行體設定執行標誌是常見的方法。另外一種方法是使用interrupt方法中斷執行緒方式關閉執行緒。 使用interrupt方法中斷執行緒來退出執行緒 sleep wait io阻塞的情況下,對於這種執行緒退出的

Jni 執行程式設計,子執行回撥java方法

由於c++層接收到服務端主動推送tcp資料,所以存在將c++層接收到的socket資料通過層層回撥至java的需求。 以下為c++程式碼段: 1:在c++標頭檔案中定義申明相應回撥函式指標 typedef void (*SwitchStateC

Java:寫2個執行,其中一個執行列印1-52,另一個執行列印A-Z,列印順序應該是12A34B56C...5152Z。

寫2個執行緒,其中一個執行緒列印1-52,另一個執行緒列印A-Z,列印順序應該是12A34B56C...5152Z   多執行緒程式設計:使用Runnable介面例項建立執行緒。使用執行緒等待方法wait(); package com.java瘋狂講義; public

MFC筆記(四)——執行程式設計2:建立執行

2.1 CreateThread() (1)函式原型:建立執行緒 HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_STA

執行程式設計指南之執行管理(iOS,Mac os )

1.執行緒成本 多執行緒會佔用記憶體和效能資源。 多執行緒另外一個需要考慮的成本是成產成本。設計一個執行緒應用有時候會需要根本性的改變你應用的資料結構的組織方式。要做這些改變可能需要避免使用同步,因為本身設計不好的應用可能會造成巨大的

Linux下的執行程式設計二(執行的同步與互斥)

一、什麼叫做執行緒的同步與互斥?為什麼需要同步與互斥? 1、同步與互斥 互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。 同步:是指在互斥的基礎上(大多數情況),通過其它機制

Linux執行程式設計學習(1)--執行的概念和執行控制

Linux多執行緒程式設計學習總結 一.執行緒的概念 1.什麼是執行緒? 2.程序和執行緒的區別 3.程序和執行緒的關係 4.執行緒的優缺點 4.1 優點 4.2 缺點 5.執行

設計一個執行超時終止執行

起因是公司有一個定時任務,對於幾千的VPN,做一個埠對映,去取得對方客戶的硬體資訊,做一個監控。 但是部分VPN會連線不通,等待的時間又過長,所以設計這麼一個執行緒池。 原貼提供了一個執行緒超時終止的實現方式,我再在這個基礎上,整理成一個執行緒池。 首先是執行緒超時終

c++11執行程式設計-程序與執行

概念:        程序:第一,程序是一個實體。每一個程序都有它自己的地址空間,一般情況下,包括文字區域(text  region)、資料區域(data region)和堆疊(stack

執行程式設計之建立執行(Windows下C++實現)

執行緒概述 理解Windows核心物件 執行緒是系統核心物件之一。在學習執行緒之前,應先了解一下核心物件。核心物件是系統核心分配的一個記憶體塊,該記憶體塊描述的是一個數據結構,其成員負責維護物件的各種資訊。核心物件的資料只能由系統核心來訪問,應用程式無法在記

Win32執行之等待一個執行的結束(WaitForSingleObject)

  由於執行緒停工是作業系統的責任,當然作業系統也有責任讓其他執行緒知道某個執行緒停工了。    Win32提供了一個名為WaitForSingleObject()的函式。他的第一個引數是個核心物件(如執行緒)的handle,為了方便討論,我把即將等待的執行緒稱為執行緒#1

使用定時器2秒後終止一個執行

使用最簡便的方法終止執行緒。 class Thread1 extends Thread{ private int count = 5; private String name; public Thread1(String name)

執行程式設計學習筆記——執行同步(三)

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //引入執行緒 using System.Diagnostics; namesp

linux執行程式設計(有關執行操作的函式)

#include <pthread.h> int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg); int pth

執行之join()方法---(Thread提供的讓一個執行等待另一個執行完成的方法)

package Demo1; /** * Created by Petty on 2017/4/9. */ public class Thread_1 extends Thread {

一個執行控制另一個執行的暫停或啟動

MainTest類中可以控制執行緒的暫停或繼續執行。 public class MainTest { /** * 這個執行緒操作另一個執行緒的暫停或開始 * @param args */ public static void main(String[] args) {