Java多線程 - 如何正確的終止線程

最近打算讀一下《實戰java高并發程序設計》,夯實一下java多線程的知識。接下來應該會寫一系列的讀書筆記,里面會有多處引用到書中的代碼或者文字。本文就是第一篇。

不推薦使用的stop方法

Thread.stop()是一個被廢棄的方法,不被推薦使用的原因是stop方法太過于暴力,強行把執行到一半的線程終止,并且會立即釋放這個線程所有的鎖。會破壞了線程中引用對象的一致性。

例如在數據庫中維護著一張用戶表,記錄了用戶ID和用戶名,使用Thread多線程寫入兩條記錄:

記錄1: ID=1,NAME=小明  
記錄2: ID=2,NAME=小王

如果在記錄1寫到一半的時候被stop結束了,就可能出現各種奇怪的現象:

  1. 記錄1被記錄2覆蓋,沒有任何數據留下。
  2. 記錄1只有一半,即只有ID,而NAME為空。
  3. 記錄1和記錄2混在同一條記錄中,最后寫入了一條一半是記錄1一半是記錄2的臟數據

所以,除非你很確定你在做什么,否則不要輕易使用stop方法

使用判斷標志位的方法中斷線程

那如果的確有中斷線程的需求,我們需要怎么做呢?一般我們馬上就會想到設置標志位的方法,即在線程中執行每一個步驟之前都判斷一下是否需要退出線程:

class WorkThread extends Thread {
    private boolean mExitThread = false;

    public void exitThread() {
        mExitThread = true;
    }

    @Override
    public void run() {
        if (mExitThread) {
            return;
        }
        //do something

        if (mExitThread) {
            return;
        }
        //do something

        if (mExitThread) {
            return;
        }
        //do something
    }
}

其實Thread類早就幫我們實現了這個中斷標志了。與Thread中斷相關的方法有下面三個:

public void Thread.interrupt() //線程中斷
public native boolean Thread.isInterrupted() //判斷是否被中斷
public static native boolean Thread.interrupted() //判斷是否中斷,并清除當前中斷狀態

所以上面的代碼可以改寫成這樣:

class WorkThread extends Thread {
    @Override
    public void run() {
        if (isInterrupted()) {
            return;
        }
        //do something

        if (isInterrupted()) {
            return;
        }
        //do something

        if (isInterrupted()) {
            return;
        }
        //do something
    }
}

這個時候我們只需要調用Thread.interrupt()方法就能安全的中斷線程了。

需要提醒一下的是Thread.interrupt()方法并不會像Thread.stop()方法一樣立即結束線程,它只是設置了一個中斷標志,需要在代碼實現中去手動判斷這個標志并且推出。

像下面這個代碼就算掉了Thread.interrupt()方法也不會中斷線程:

class WorkThread extends Thread {
    @Override
    public void run() {
        while(true){
            //do something
        }
    }
}

Thread.interrupt的優點

使用Thread.interrupt去中斷線程除了可以免去自己實現標志位的煩惱之外,還可以中斷sleep和wait中的線程。

還記得我們在調用Thread.sleep()這個方法的時候都需要catch或者拋出InterruptedException這個異常嗎:

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

這個InterruptedException異常就是由于我們調用了Thread.interrupt方法拋出的。所以Thread.interrupt可以打斷Thread.sleep。同樣Thread.wait也是可以被Thread.interrupt打斷的。

需要注意的是如果sleep方法由于中斷而拋出異常,此時,它會清除中斷標記。所以在catch到這個異常的時候需要再次設置中斷標記

Thread t1 = new Thread() {
    @Override
    public void run(){
        while(true){
            if(Thread.currentThread().isInterrupted()){
                break;
            }

            System.out.println("hello world");
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
};
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容