之前講到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()來判斷線程是否中斷。