1,線程的生命周期
線程從創建、運行到結束總是處于下面五個狀態之一:新建狀態、就緒狀態、運行狀態、阻塞狀態及死亡狀態。

1.新建狀態(New):
當用new操作符創建一個線程時, 例如new Thread(r),線程還沒有開始運行,此時線程處在新建狀態。 當一個線程處于新生狀態時,程序還沒有開始運行線程中的代碼
2.就緒狀態(Runnable)
一個新創建的線程并不自動開始運行,要執行線程,必須調用線程的start()方法。當線程對象調用start()方法即啟動了線程,start()方法創建線程運行的系統資源,并調度線程運行run()方法。當start()方法返回后,線程就處于就緒狀態。
處于就緒狀態的線程并不一定立即運行run()方法,線程還必須同其他線程競爭CPU時間,只有獲得CPU時間才可以運行線程。因為在單CPU的計算機系統中,不可能同時運行多個線程,一個時刻僅有一個線程處于運行狀態。因此此時可能有多個線程處于就緒狀態。對多個處于就緒狀態的線程是由Java運行時系統的線程調度程序(thread scheduler)來調度的。
3.運行狀態(Running)
當線程獲得CPU時間后,它才進入運行狀態,真正開始執行run()方法.
4. 阻塞狀態(Blocked)
線程運行過程中,可能由于各種原因進入阻塞狀態:
1>線程通過調用sleep方法進入睡眠狀態;
2>線程調用一個在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會返回到它的調用者;
3>線程試圖得到一個鎖,而該鎖正被其他線程持有;
4>線程在等待某個觸發條件;
......
所謂阻塞狀態是正在運行的線程沒有運行結束,暫時讓出CPU,這時其他處于就緒狀態的線程就可以獲得CPU時間,進入運行狀態。
5. 死亡狀態(Dead)
有兩個原因會導致線程死亡:
1) run方法正常退出而自然死亡,
2) 一個未捕獲的異常終止了run方法而使線程猝死。
為了確定線程在當前是否存活著(就是要么是可運行的,要么是被阻塞了),需要使用isAlive方法。如果是可運行或被阻塞,這個方法返回true; 如果線程仍舊是new狀態且不是可運行的, 或者線程死亡了,則返回false.
2, 線程的優先級和調度
Java的每個線程都有一個優先級,當有多個線程處于就緒狀態時,線程調度程序根據線程的優先級調度線程運行。
可以用下面方法設置和返回線程的優先級。
· public final void setPriority(int newPriority) 設置線程的優先級。
· public final int getPriority() 返回線程的優先級。
newPriority為線程的優先級,其取值為1到10之間的整數,也可以使用Thread類定義的常量來設置線程的優先級,這些常量分別為:Thread.MIN_PRIORITY、Thread.NORM_PRIORITY、Thread.MAX_PRIORITY,它們分別對應于線程優先級的1、5和10,數值越大優先級越高。當創建Java線程時,如果沒有指定它的優先級,則它從創建該線程那里繼承優先級。
一般來說,只有在當前線程停止或由于某種原因被阻塞,較低優先級的線程才有機會運行。
前面說過多個線程可并發運行,然而實際上并不總是這樣。由于很多計算機都是單CPU的,所以一個時刻只能有一個線程運行,多個線程的并發運行只是幻覺。在單CPU機器上多個線程的執行是按照某種順序執行的,這稱為線程的調度(scheduling)。
大多數計算機僅有一個CPU,所以線程必須與其他線程共享CPU。多個線程在單個CPU是按照某種順序執行的。實際的調度策略隨系統的不同而不同,通常線程調度可以采用兩種策略調度處于就緒狀態的線程。
(1) 搶占式調度策略
Java運行時系統的線程調度算法是搶占式的 (preemptive)。Java運行時系統支持一種簡單的固定優先級的調度算法。如果一個優先級比其他任何處于可運行狀態的線程都高的線程進入就緒狀態,那么運行時系統就會選擇該線程運行。新的優先級較高的線程搶占(preempt)了其他線程。但是Java運行時系統并不搶占同優先級的線程。換句話說,Java運行時系統不是分時的(time-slice)。然而,基于Java Thread類的實現系統可能是支持分時的,因此編寫代碼時不要依賴分時。當系統中的處于就緒狀態的線程都具有相同優先級時,線程調度程序采用一種簡單的、非搶占式的輪轉的調度順序。
(2) 時間片輪轉調度策略
有些系統的線程調度采用時間片輪轉(round-robin)調度策略。這種調度策略是從所有處于就緒狀態的線程中選擇優先級最高的線程分配一定的CPU時間運行。該時間過后再選擇其他線程運行。只有當線程運行結束、放棄(yield)CPU或由于某種原因進入阻塞狀態,低優先級的線程才有機會執行。如果有兩個優先級相同的線程都在等待CPU,則調度程序以輪轉的方式選擇運行的線程。
3. 線程狀態的改變
一個線程在其生命周期中可以從一種狀態改變到另一種狀態,線程狀態的變遷如圖所示:

1> 控制線程的啟動和結束
當一個新建的線程調用它的start()方法后即進入就緒狀態,處于就緒狀態的線程被線程調度程序選中就可以獲得CPU時間,進入運行狀態,該線程就開始運行run()方法。
控制線程的結束稍微復雜一點。如果線程的run()方法是一個確定次數的循環,則循環結束后,線程運行就結束了,線程對象即進入死亡狀態。如果run()方法是一個不確定循環,早期的方法是調用線程對象的stop()方法,然而由于該方法可能導致線程死鎖,因此從1.1版開始,不推薦使用該方法結束線程。一般是通過設置一個標志變量,在程序中改變標志變量的值實現結束線程。請看下面的例子:
程序 ThreadStop.java
import java.util.*;
class Timer implements Runnable{
boolean flag=true;
public void run(){
while(flag){
System.out.print("\r\t"+new Date()+"
");
try{
Thread.sleep(1000);
}catch(InterruptedException e){}
}
System.out.println("\n"+Thread.currentThread().getName()+" Stop");
}
public void stopRun(){
flag = false;
}
}
public class ThreadStop{
public static void main(String args[]){
Timer timer = new Timer();
Thread thread = new Thread(timer);
thread.setName("Timer");
thread.start();
for(int i=0;i<100;i++){
System.out.print("\r"+i);
try{
Thread.sleep(100);
}catch(InterruptedException e){}
}
timer.stopRun();
}
}
該程序在Timer類中定義了一個布爾變量flag,同時定義了一個stopRun()方法,在其中將該變量設置為false。在主程序中通過調用該方法,從而改變該變量的值,使得run()方法的while循環條件不滿足,從而實現結束線程的運行。
說明 在Thread類中除了stop()方法被標注為不推薦(deprecated) 使用外,suspend()方法和resume()方法也被標明不推薦使用,這兩個方法原來用作線程的掛起和恢復.
2> 線程阻塞條件
處于運行狀態的線程除了可以進入死亡狀態外,還可能進入就緒狀態和阻塞狀態。下面分別討論這兩種情況:
(1) 運行狀態到就緒狀態
處于運行狀態的線程如果調用了yield()方法,那么它將放棄CPU時間,使當前正在運行的線程進入就緒狀態。這時有幾種可能的情況:如果沒有其他的線程處于就緒狀態等待運行,該線程會立即繼續運行;如果有等待的線程,此時線程回到就緒狀態狀態與其他線程競爭CPU時間,當有比該線程優先級高的線程時,高優先級的線程進入運行狀態,當沒有比該線程優先級高的線程時,但有同優先級的線程,則由線程調度程序來決定哪個線程進入運行狀態,因此線程調用yield()方法只能將CPU時間讓給具有同優先級的或高優先級的線程而不能讓給低優先級的線程。
一般來說,在調用線程的yield()方法可以使耗時的線程暫停執行一段時間,使其他線程有執行的機會。
(2) 運行狀態到阻塞狀態
有多種原因可使當前運行的線程進入阻塞狀態,進入阻塞狀態的線程當相應的事件結束或條件滿足時進入就緒狀態。使線程進入阻塞狀態可能有多種原因:
① 線程調用了sleep()方法,線程進入睡眠狀態,此時該線程停止執行一段時間。當時間到時該線程回到就緒狀態,與其他線程競爭CPU時間。
Thread類中定義了一個interrupt()方法。一個處于睡眠中的線程若調用了interrupt()方法,該線程立即結束睡眠進入就緒狀態。
② 如果一個線程的運行需要進行I/O操作,比如從鍵盤接收數據,這時程序可能需要等待用戶的輸入,這時如果該線程一直占用CPU,其他線程就得不到運行。這種情況稱為I/O阻塞。這時該線程就會離開運行狀態而進入阻塞狀態。Java語言的所有I/O方法都具有這種行為。
③ 有時要求當前線程的執行在另一個線程執行結束后再繼續執行,這時可以調用join()方法實現,join()方法有下面三種格式:
· public void join() throws InterruptedException 使當前線程暫停執行,等待調用該方法的線程結束后再執行當前線程。
· public void join(long millis) throws InterruptedException 最多等待millis毫秒后,當前線程繼續執行。
· public void join(long millis, int nanos) throws InterruptedException 可以指定多少毫秒、多少納秒后繼續執行當前線程。
上述方法使當前線程暫停執行,進入阻塞狀態,當調用線程結束或指定的時間過后,當前線程線程進入就緒狀態,例如執行下面代碼:
t.join();
將使當前線程進入阻塞狀態,當線程t執行結束后,當前線程才能繼續執行。
④ 線程調用了wait()方法,等待某個條件變量,此時該線程進入阻塞狀態。直到被通知(調用了notify()或notifyAll()方法)結束等待后,線程回到就緒狀態。
⑤ 另外如果線程不能獲得對象鎖,也進入就緒狀態。
后兩種情況在下一節討論。