一步一步掌握java的線程機制(二)----Thread的生命周期


      之前講到Thread的創建,那是Thread生命周期的第一步,其后就是通過start()方法來啟動Thread,它會執行一些內部的管理工作然后調用Thread的run()方法,此時該Thread就是alive(活躍)的,而且我們還可以通過isAlive()方法來確定該線程是否啟動還是終結。

      一旦啟動Thread后,我們就只能執行一個方法:run(),而run()方法就是負責執行Thread的任務,所以終結Thread的方法很簡單,就是終結run()方法。仔細查看文檔,我們會發現里面有一個方法:stop(),似乎可以用來停止Thread,但是這個方法已經被廢除了,因為它存在着內部的競爭。

      我們經常需要一個不斷執行的Thread,然后在某個特定的條件下才會終結它,方法有很多,但最常用的有設定標記和中斷Thread兩種方式。

      我們將之前例子中的Thread改寫一下:

public class RandomCharacterGenerator extends Thread implements CharacterSource {
    static char[] chars;
    static String charArray = "abcdefghijklmnopqrstuvwxyz0123456789";
    static {
        chars = charArray.toCharArray();
    }
    
    private volatile boolean done = false;

    Random random;
    CharacterEventHandler handler;

    public RandomCharacterGenerator() {
        random = new Random();
        handler = new CharacterEventHandler();
    }

    public int getPauseTime() {
        return (int) (Math.max(1000, 5000 * random.nextDouble()));
    }

    @Override
    public void addCharacterListener(CharacterListener cl) {
        handler.addCharacterListener(cl);
    }

    @Override
    public void removeCharacterListener(CharacterListener cl) {
        handler.removeCharacterListener(cl);
    }

    @Override
    public void nextCharacter() {
        handler.fireNewCharacter(this,
                (int) chars[random.nextInt(chars.length)]);
    }

    public void run() {
        while(!done){
            nextCharacter();
            try {
                Thread.sleep(getPauseTime());
            } catch (InterruptedException ie) {
                return;
            }
        }
    }
    
    public void setDone(){
        done = true;
    }
}

      現在我們多了一個標記:done,這樣我們就可以在代碼中通過調用setDone()來決定什么時候停止該Thread。這里使用了volatile關鍵字,它主要是為了同步。這點會放在同步這里講。
      設定標記的最大問題就是我們必須等待標記的狀態,這樣就會造成延遲。當然,這種延遲是無法避免的,但必須想辦法縮短到最小。於是,中斷Thread這種方法就有它的發揮地方了。

      我們可以通過interrupt()方法來中斷Thread,該方法會造成兩個副作用:

1.它會導致任何的阻塞方法都會拋出InterruptedException,我們必須強制性的捕獲這個錯誤哪怕我們根本就不需要處理它,這也是java的異常處理機制讓人詬病的一個地方。

2.設定Thread對象內部的標記來指示此Thread已經被中斷了,像是這樣:

public void run(){
     while(!isInterrupted()){
        ...
      }
}

      雖然無法避免延遲,但是延遲已經被縮短了。
      無論是采用標記還是中斷的方法,我們之所以無法消除延遲的原因是我們無法確定是檢查標記先還是調用方法先,這就是所謂的race condition,是線程處理中永遠無法避免的話題。

      Thread不僅可以被終結,還可以暫停,掛起和恢復。

      Thread原本有suspend()方法和resume()方法來執行掛起和恢復,但它們和stop()出於同樣的原因,都被廢除了。

      我們可以通過sleep()方法來掛起Thread,當在指定的時間后,它就會自動恢復。嚴格意義上講,sleep並不等同於suspend,真正的suspend應該是由一個線程來掛起另一個線程,但是sleep只會影響當前的Thread。要想真正實現掛起和恢復,我們可以使用等待和通知機制,但這個機制最大的問題就是我們的Thread必須使用該技術來編寫。

      Thread在終結后,如果有可能,我們還需要對它進行善后。即使Thread已經被終結了,但是其他對象只要還持有它的引用,它們就可以調用該Thread的資源,這也會導致該Thread無法被回收。

       但我們有時候還是希望繼續保持該Thread的引用,因為我們想要判別它是否真的已經完成了工作,可以使用join()方法。join()方法會被阻塞住直到Thread完成它的run()方法,但是這個存在風險:第一次對join()方法的調用可能會一直被阻塞住很長時間直到Thread真正完成,所以,一般情況下我們還是使用isAlive()方法來判斷。

       由於我們可以通過實現一個Runnable接口來定義我們的任務,所以在判斷所在線程是否已經中斷的時候,就有一個問題:該任務還沒有綁定到任何線程上。我們可以通過currentThread()方法來獲得當前Thread的引用,接着調用isInterrupted()來判斷線程是否中斷。

      

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM