3.1 線程狀态概述
當線程被建立并啟動以後,它既不是一啟動就進入了執行狀态,也不是一直處于執行狀态。線上程的生命周期中, 有幾種狀态呢?在API中 java.lang.Thread.State 這個枚舉中給出了六種線程狀态:
這裡先列出各個線程狀态發生的條件,下面将會對每種狀态進行詳細解析

我們不需要去研究這幾種狀态的實作原理,我們隻需知道在做線程操作中存在這樣的狀态。那我們怎麼去了解這幾 個狀态呢,建立與被終止還是很容易了解的,我們就研究一下線程從Runnable(可運作)狀态與非運作狀态之間 的轉換問題。
3.2 Timed Waiting(計時等待)
Timed Waiting在API中的描述為:一個正在限時等待另一個線程執行一個(喚醒)動作的線程處于這一狀态。單獨 的去了解這句話,真是玄之又玄,其實我們在之前的操作中已經接觸過這個狀态了,在哪裡呢?
在我們寫賣票的案例中,為了減少線程執行太快,現象不明顯等問題,我們在run方法中添加了sleep語句,這樣就 強制目前正在執行的線程休眠(暫停執行),以“減慢線程”。
其實當我們調用了sleep方法之後,目前執行的線程就進入到“休眠狀态”,其實就是所謂的Timed Waiting(計時等 待),那麼我們通過一個案例加深對該狀态的一個了解。
實作一個計數器,計數到100,在每個數字之間暫停1秒,每隔10個數字輸出一個字元串
代碼:
public class MyThread extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
if ((i) % 10 == 0) {
System.out.println("‐‐‐‐‐‐‐" + i);
}
System.out.print(i);
try {
Thread.sleep(1000);
System.out.print(" 線程睡眠1秒!\n");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new MyThread().start();
}
}
通過案例可以發現,sleep方法的使用還是很簡單的。我們需要記住下面幾點:
- 進入 TIMED_WAITING 狀态的一種常見情形是調用的 sleep 方法,單獨的線程也可以調用,不一定非要有協 作關系。
- 為了讓其他線程有機會執行,可以将Thread.sleep()的調用放線程run()之内。這樣才能保證該線程執行過程 中會睡眠
- sleep與鎖無關,線程睡眠到期自動蘇醒,并傳回到Runnable(可運作)狀态。
小提示:sleep()中指定的時間是線程不會運作的最短時間。是以,sleep()方法不能保證該線程睡眠到期後就 開始立刻執行。
Timed Waiting 線程狀态圖:
3.3 BLOCKED(鎖阻塞)
Blocked狀态在API中的介紹為:一個正在阻塞等待一個螢幕鎖(鎖對象)的線程處于這一狀态。
我們已經學完同步機制,那麼這個狀态是非常好了解的了。比如,線程A與線程B代碼中使用同一鎖,如果線程A獲 取到鎖,線程A進入到Runnable狀态,那麼線程B就進入到Blocked鎖阻塞狀态。
這是由Runnable狀态進入Blocked狀态。除此Waiting以及Time Waiting狀态也會在某種情況下進入阻塞狀态,而 這部分内容作為擴充知識點帶領大家了解一下。
Blocked 線程狀态圖
3.4 Waiting(無限等待)
Wating狀态在API中介紹為:一個正在無限期等待另一個線程執行一個特别的(喚醒)動作的線程處于這一狀态。
那麼我們之前遇到過這種狀态嗎?答案是并沒有,但并不妨礙我們進行一個簡單深入的了解。我們通過一段代碼來 學習一下:
public class WaitingTest {
public static Object obj = new Object();
public static void main(String[] args) {
// 示範waiting
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
try {
System.out.println(Thread.currentThread().getName() + "=== 擷取到鎖對象,調用wait方法,進入waiting狀态,釋放鎖對象");
obj.wait();
//無限等待
obj.wait(5000);
//計時等待, 5秒 時間到,自動醒來
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "=== 從waiting狀 态醒來,擷取到鎖對象,繼續執行了");
}
}
}
}, "等待線程").start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
//每隔3秒 喚醒一次
try {
System.out.println(Thread.currentThread().getName() + "‐‐‐‐‐ 等待3秒鐘");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {
System.out.println(Thread.currentThread().getName() + "‐‐‐‐‐ 擷取到鎖對 象,調用notify方法,釋放鎖對象");
obj.notify();
}
}
}
}, "喚醒線程").start();
}
}
通過上述案例我們會發現,一個調用了某個對象的 Object.wait 方法的線程會等待另一個線程調用此對象的 Object.notify()方法 或 Object.notifyAll()方法。
其實waiting狀态并不是一個線程的操作,它展現的是多個線程間的通信,可以了解為多個線程之間的協作關系, 多個線程會争取鎖,同時互相之間又存在協作關系。就好比在公司裡你和你的同僚們,你們可能存在晉升時的競 争,但更多時候你們更多是一起合作以完成某些任務。
當多個線程協作時,比如A,B線程,如果A線程在Runnable(可運作)狀态中調用了wait()方法那麼A線程就進入 了Waiting(無限等待)狀态,同時失去了同步鎖。假如這個時候B線程擷取到了同步鎖,在運作狀态中調用了 notify()方法,那麼就會将無限等待的A線程喚醒。注意是喚醒,如果擷取到鎖對象,那麼A線程喚醒後就進入 Runnable(可運作)狀态;如果沒有擷取鎖對象,那麼就進入到Blocked(鎖阻塞狀态)。