1. 程式人生 > >Java Thread系列(二)線程狀態

Java Thread系列(二)線程狀態

做的 tor throws 前臺 bject 線程休眠 enume 死鎖 做出

Java Thread系列(二)線程狀態

一、線程的五種狀態

技術分享圖片

  1. 新建狀態(New):新創建了一個線程對象,尚未啟動。
  2. 就緒狀態(Runnable):也叫可運行狀態。線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待獲取 CPU 的使用權。
  3. 運行狀態(Running):就緒狀態的線程獲取了 CPU,執行程序代碼。
  4. 阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄 CPU 使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:
    • 等待阻塞:運行的線程執行 wait() 方法,JVM 會把該線程放入等待池中。
    • 同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則 JVM 會把該線程放入鎖池中。
    • 其他阻塞:運行的線程執行 sleep() 或 join() 方法,或者發出了 I/O 請求時,JVM 會把該線程置為阻塞狀態。當 sleep() 狀態超時、join() 等待線程終止或者超時、或者 I/O 處理完畢時,線程重新轉入就緒狀態。
  5. 死亡狀態(Dead):線程執行完了或者因異常退出了 run() 方法,該線程結束生命周期。

二、線程調度

(1) 線程優先級

Java 線程有優先級,優先級高的線程會獲得較多的運行機會。

Java 線程的優先級用整數表示,取值範圍是 1~10,Thread 類有以下三個靜態常量:

static int MAX_PRIORITY     // 線程可以具有的最高優先級,取值為 10。
static int MIN_PRIORITY     // 線程可以具有的最低優先級,取值為 1。
static int NORM_PRIORITY    // 分配給線程的默認優先級,取值為 5。

Thread 類的 setPriority() 和 getPriority() 方法分別用來設置和獲取線程的優先級。

每個線程都有默認的優先級。主線程的默認優先級為 Thread.NORM_PRIORITY。
線程的優先級有繼承關系,比如 A 線程中創建了 B 線程,那麽 B 將和 A 具有相同的優先級。
JVM 提供了 10 個線程優先級,但與常見的操作系統都不能很好的映射。如果希望程序能移植到各個操作系統中,應該僅僅使用 Thread 類有以下三個靜態常量作為優先級,這樣能保證同樣的優先級采用了同樣的調度方式。

(2) 線程睡眠

Thread.sleep(long millis) 方法,使線程轉到阻塞狀態。millis 參數設定睡眠的時間,以毫秒為單位。當睡眠結束後,就轉為就緒(Runnable)狀態。sleep() 平臺移植性好。

(3) 線程等待

Object 類中的 wait() 方法,導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 喚醒方法。這個兩個喚醒方法也是 Object 類中的方法,行為等價於調用 wait(0) 一樣。

(4) 線程讓步

Thread.yield() 方法,暫停當前正在執行的線程對象,把執行機會讓給相同或者更高優先級的線程。

(5) 線程 join

join() 方法,等待其他線程終止。在當前線程中調用另一個線程的 join()方法,則當前線程轉入阻塞狀態,直到另一個進程運行結束,當前線程再由阻塞轉為就緒狀態。

(6) 線程喚醒

Object 類中的 notify() 方法,喚醒在此對象監視器上等待的單個線程。如果所有線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,並在對實現做出決定時發生。線程通過調用其中一個 wait 方法,在對象的監視器上等待。 直到當前的線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程。被喚醒的線程將以常規方式與在該對象上主動同步的其他所有線程進行競爭;例如,喚醒的線程在作為鎖定此對象的下一個線程方面沒有可靠的特權或劣勢。類似的方法還有一個 notifyAll(),喚醒在此對象監視器上等待的所有線程。

註意:Thread 中 suspend() 和 resume() 兩個方法在 JDK1.5 中已經廢除,不再介紹。因為有死鎖傾向。

三、常用函數說明

(1) Thread.sleep(long millis)

sleep() 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)

sleep() 不能改變對象的機鎖,所以當在一個 Synchronized 塊中調用 Sleep() 方法時,線程雖然休眠了,但是對象的機鎖並木有被釋放,其他線程無法訪問這個對象(即使睡著也持有對象鎖)。

在 sleep() 休眠時間期滿後,該線程不一定會立即執行,這是因為其它線程可能正在運行而且沒有被調度為放棄執行,除非此線程具有更高的優先級。

(2) join()

public class JoinTest extends Thread {

    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            System.out.println("子線程:" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            ;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        JoinTest t = new JoinTest();
        t.start();
        t.join();   // (1)

        System.out.println("main 線程:" + Thread.currentThread().getName());
    }
}
  1. t.join() 會阻塞當前線程,即 main 線程,也就是說 main 線程會等待子線程執行完畢才會繼續往下執行。

(3) Thread.yield()

Thread.yield() 方法作用是:暫停當前正在執行的線程對象,並執行其他線程。

Thread.yield() 應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用 Thread.yield() 的目的是讓相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證 yield() 達到讓步目的,因為讓步的線程還有可能被線程調度程序再次選中。

(4) setPriority()

更改線程的優先級。

MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10

(5) interrupt()

不要以為它是中斷某個線程!它只是線線程發送一個中斷信號,讓線程在無限等待時(如死鎖時)能拋出異常,從而結束線程,但是如果你吃掉了這個異常,那麽這個線程還是不會中斷的!

(6) wait()

wait() 方法是 Object 類裏的方法;當一個線程執行到 wait() 方法時,它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖(暫時失去機鎖,wait(long timeout)超時時間到後還需要返還對象鎖);其他線程可以訪問;

wait() 使用 notify 或者 notifyAlll 或者指定睡眠時間來喚醒當前等待池中的線程。

wiat() 必須放在 synchronized block 中,否則會在 program runtime 時扔出 “java.lang.IllegalMonitorStateException” 異常。

wait 和 sleep 區別

共同點:

  1. 他們都是在多線程的環境下,都可以在程序的調用處阻塞指定的毫秒數,並返回。
  2. wait() 和 sleep() 都可以通過 interrupt() 方法 打斷線程的暫停狀態 ,從而使線程立刻拋出 InterruptedException。

如果線程 A 希望立即結束線程 B,則可以對線程 B 對應的 Thread 實例調用 interrupt 方法。如果此刻線程 B 正在 wait/sleep /join,則線程 B 會立刻拋出 InterruptedException,在 catch() {} 中直接 return 即可安全地結束線程。

需要註意的是,InterruptedException 是線程自己從內部拋出的,並不是 interrupt() 方法拋出的。對某一線程調用 interrupt() 時,如果該線程正在執行普通的代碼,那麽該線程根本就不會拋出 InterruptedException。但是,一旦該線程進入到 wait()/sleep()/join() 後,就會立刻拋出 InterruptedException 。

不同點:

  1. Thread 類的方法:sleep(),yield() 等。Object的方法:wait() 和 notify() 等
  2. 每個對象都有一個鎖來控制同步訪問。Synchronized 關鍵字可以和對象的鎖交互,來實現線程的同步。
    sleep 方法沒有釋放鎖,而 wait 方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。
  3. wait,notify 和 notifyAll 只能在同步控制方法或者同步控制塊裏面使用,而 sleep 可以在任何地方使用
  4. sleep 必須捕獲異常,而 wait,notify 和 notifyAll 不需要捕獲異常

所以 Thread.sleep() 與 Object.wait() 方法的最大區別是:sleep()睡眠時,保持對象鎖,仍然占有該鎖;而wait() 睡眠時,釋放對象鎖。但是 wait() 和 sleep() 都可以通過 interrupt() 方法打斷線程的暫停狀態,從而使線程立刻拋出 InterruptedException(但不建議使用該方法)。

四、常見線程名詞解釋

主線程:JVM 調用程序 main() 所產生的線程。

當前線程:這個是容易混淆的概念。一般指通過 Thread.currentThread() 來獲取的進程。

後臺線程(守護線程):指為其他線程提供服務的線程。JVM 的垃圾回收線程就是一個後臺線程。用戶線程和守護線程的區別在於,是否等待主線程依賴於主線程結束而結束.

前臺線程:是指接受後臺線程服務的線程,其實前臺後臺線程是聯系在一起,就像傀儡和幕後操縱者一樣的關系。傀儡是前臺線程、幕後操縱者是後臺線程。由前臺線程創建的線程默認也是前臺線程。可以通過 isDaemon() 和 setDaemon() 方法來判斷和設置一個線程是否為後臺線程。

Thread 線程類的一些常用方法:

Thread.sleep(): 強迫一個線程睡眠N毫秒。 
isAlive(): 判斷一個線程是否存活。 
join(): 等待線程終止。 
activeCount(): 程序中活躍的線程數。 
enumerate(): 枚舉程序中的線程。 
currentThread(): 得到當前線程。 
isDaemon(): 一個線程是否為守護線程。 
setDaemon(): 設置一個線程為守護線程。(用戶線程和守護線程的區別在於,是否等待主線程依賴於主線程結束而結束) 
setName(): 為線程設置一個名稱。 
wait(): 強迫一個線程等待。 
notify(): 通知一個線程繼續運行。 
setPriority(): 設置一個線程的優先級。

每天用心記錄一點點。內容也許不重要,但習慣很重要!

Java Thread系列(二)線程狀態