1,線程的生命周期
線程從創(chuàng)建、運(yùn)行到結(jié)束總是處于下面五個(gè)狀態(tài)之一:新建狀態(tài)、就緒狀態(tài)、運(yùn)行狀態(tài)、阻塞狀態(tài)及死亡狀態(tài)。

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

1> 控制線程的啟動(dòng)和結(jié)束
當(dāng)一個(gè)新建的線程調(diào)用它的start()方法后即進(jìn)入就緒狀態(tài),處于就緒狀態(tài)的線程被線程調(diào)度程序選中就可以獲得CPU時(shí)間,進(jìn)入運(yùn)行狀態(tài),該線程就開(kāi)始運(yùn)行run()方法。
控制線程的結(jié)束稍微復(fù)雜一點(diǎn)。如果線程的run()方法是一個(gè)確定次數(shù)的循環(huán),則循環(huán)結(jié)束后,線程運(yùn)行就結(jié)束了,線程對(duì)象即進(jìn)入死亡狀態(tài)。如果run()方法是一個(gè)不確定循環(huán),早期的方法是調(diào)用線程對(duì)象的stop()方法,然而由于該方法可能導(dǎo)致線程死鎖,因此從1.1版開(kāi)始,不推薦使用該方法結(jié)束線程。一般是通過(guò)設(shè)置一個(gè)標(biāo)志變量,在程序中改變標(biāo)志變量的值實(shí)現(xiàn)結(jié)束線程。請(qǐng)看下面的例子:
程序 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類(lèi)中定義了一個(gè)布爾變量flag,同時(shí)定義了一個(gè)stopRun()方法,在其中將該變量設(shè)置為false。在主程序中通過(guò)調(diào)用該方法,從而改變?cè)撟兞康闹?,使?/span>run()方法的while循環(huán)條件不滿(mǎn)足,從而實(shí)現(xiàn)結(jié)束線程的運(yùn)行。
說(shuō)明 在Thread類(lèi)中除了stop()方法被標(biāo)注為不推薦(deprecated) 使用外,suspend()方法和resume()方法也被標(biāo)明不推薦使用,這兩個(gè)方法原來(lái)用作線程的掛起和恢復(fù).
2> 線程阻塞條件
處于運(yùn)行狀態(tài)的線程除了可以進(jìn)入死亡狀態(tài)外,還可能進(jìn)入就緒狀態(tài)和阻塞狀態(tài)。下面分別討論這兩種情況:
(1) 運(yùn)行狀態(tài)到就緒狀態(tài)
處于運(yùn)行狀態(tài)的線程如果調(diào)用了yield()方法,那么它將放棄CPU時(shí)間,使當(dāng)前正在運(yùn)行的線程進(jìn)入就緒狀態(tài)。這時(shí)有幾種可能的情況:如果沒(méi)有其他的線程處于就緒狀態(tài)等待運(yùn)行,該線程會(huì)立即繼續(xù)運(yùn)行;如果有等待的線程,此時(shí)線程回到就緒狀態(tài)狀態(tài)與其他線程競(jìng)爭(zhēng)CPU時(shí)間,當(dāng)有比該線程優(yōu)先級(jí)高的線程時(shí),高優(yōu)先級(jí)的線程進(jìn)入運(yùn)行狀態(tài),當(dāng)沒(méi)有比該線程優(yōu)先級(jí)高的線程時(shí),但有同優(yōu)先級(jí)的線程,則由線程調(diào)度程序來(lái)決定哪個(gè)線程進(jìn)入運(yùn)行狀態(tài),因此線程調(diào)用yield()方法只能將CPU時(shí)間讓給具有同優(yōu)先級(jí)的或高優(yōu)先級(jí)的線程而不能讓給低優(yōu)先級(jí)的線程。
一般來(lái)說(shuō),在調(diào)用線程的yield()方法可以使耗時(shí)的線程暫停執(zhí)行一段時(shí)間,使其他線程有執(zhí)行的機(jī)會(huì)。
(2) 運(yùn)行狀態(tài)到阻塞狀態(tài)
有多種原因可使當(dāng)前運(yùn)行的線程進(jìn)入阻塞狀態(tài),進(jìn)入阻塞狀態(tài)的線程當(dāng)相應(yīng)的事件結(jié)束或條件滿(mǎn)足時(shí)進(jìn)入就緒狀態(tài)。使線程進(jìn)入阻塞狀態(tài)可能有多種原因:
① 線程調(diào)用了sleep()方法,線程進(jìn)入睡眠狀態(tài),此時(shí)該線程停止執(zhí)行一段時(shí)間。當(dāng)時(shí)間到時(shí)該線程回到就緒狀態(tài),與其他線程競(jìng)爭(zhēng)CPU時(shí)間。
Thread類(lèi)中定義了一個(gè)interrupt()方法。一個(gè)處于睡眠中的線程若調(diào)用了interrupt()方法,該線程立即結(jié)束睡眠進(jìn)入就緒狀態(tài)。
② 如果一個(gè)線程的運(yùn)行需要進(jìn)行I/O操作,比如從鍵盤(pán)接收數(shù)據(jù),這時(shí)程序可能需要等待用戶(hù)的輸入,這時(shí)如果該線程一直占用CPU,其他線程就得不到運(yùn)行。這種情況稱(chēng)為I/O阻塞。這時(shí)該線程就會(huì)離開(kāi)運(yùn)行狀態(tài)而進(jìn)入阻塞狀態(tài)。Java語(yǔ)言的所有I/O方法都具有這種行為。
③ 有時(shí)要求當(dāng)前線程的執(zhí)行在另一個(gè)線程執(zhí)行結(jié)束后再繼續(xù)執(zhí)行,這時(shí)可以調(diào)用join()方法實(shí)現(xiàn),join()方法有下面三種格式:
· public void join() throws InterruptedException 使當(dāng)前線程暫停執(zhí)行,等待調(diào)用該方法的線程結(jié)束后再執(zhí)行當(dāng)前線程。
· public void join(long millis) throws InterruptedException 最多等待millis毫秒后,當(dāng)前線程繼續(xù)執(zhí)行。
· public void join(long millis, int nanos) throws InterruptedException 可以指定多少毫秒、多少納秒后繼續(xù)執(zhí)行當(dāng)前線程。
上述方法使當(dāng)前線程暫停執(zhí)行,進(jìn)入阻塞狀態(tài),當(dāng)調(diào)用線程結(jié)束或指定的時(shí)間過(guò)后,當(dāng)前線程線程進(jìn)入就緒狀態(tài),例如執(zhí)行下面代碼:
t.join();
將使當(dāng)前線程進(jìn)入阻塞狀態(tài),當(dāng)線程t執(zhí)行結(jié)束后,當(dāng)前線程才能繼續(xù)執(zhí)行。
④ 線程調(diào)用了wait()方法,等待某個(gè)條件變量,此時(shí)該線程進(jìn)入阻塞狀態(tài)。直到被通知(調(diào)用了notify()或notifyAll()方法)結(jié)束等待后,線程回到就緒狀態(tài)。
⑤ 另外如果線程不能獲得對(duì)象鎖,也進(jìn)入就緒狀態(tài)。
后兩種情況在下一節(jié)討論。