原創文章, 轉載請私信. 關注 tastejava 學習加思考, 品味java之美
爲什麼要了解java線程的狀態
多線程高併發是初級開發者邁向中高級開發者必須要掌握的能力, 萬丈高樓平地起, 在深入瞭解這部分內容前, 我們要明確最基本的概念, 即線程有哪些狀態.
從源碼來看Java線程有哪些狀態
得益於開源精神, 我們可以直觀的分析看到在Java中, 線程到底有哪些狀態. Java中記錄線程所有狀態的枚舉類爲
java.lang.Thread.State
從源代碼中我們可以看到在Java中線程總共有6種狀態NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED. 下面我們來看一下詳細的源代碼, 以及各種線程狀態的含義.
public enum State {
/**
* 線程對象實例化但是還沒有調用start方法.
*/
NEW,
/**
* 線程處於可運行狀態, 這個狀態在虛擬機中看來是正在執行的, 但是實際可能在等待
* 操作系統的資源, 比如等待CPU資源. 注意Java線程狀態只有RUNNABLE而沒有RUNNING
*/
RUNNABLE,
/**
* 阻塞狀態的線程可能在等待獲取鎖, 也可能是調用了wait方法後被notify方法
* 喚醒, 再次嘗試獲取鎖, 進入阻塞狀態
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* 等待狀態, 此狀態由於調用wait, join, park方法導致
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* 線程進入等待狀態後, 等待notify, notifyAll, 特定線程執行完畢, unpark方法
* 然後轉換爲RUNNABLE狀態
*/
WAITING,
/**
* 有最大時間限制的等待狀態, 可能由調用如下方法導致
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* 終止狀態, 線程執行完畢後會進入TERMINATED狀態, 並且不能再轉換成其它狀態
*/
TERMINATED;
}
10句代碼窮盡線程的6種狀態
爲了增強記憶, 下面我們用10句有效代碼, 直觀的把Java線程的6種狀態展示出來.
/**
* 如下展示了java線程的6種狀態
* NEW 線程對象新創建還未start
* RUNNABLE 線程已經啓動, 線程在JVM中正在運行, 不過在系統層面可能在等待系統資源
* BLOCKED 線程等待獲取鎖
* WAITING 線程被LockSupport#park(),
* Object#wait()或者Thread.join, 等待被unpark或者notify或者其他新城join完畢
* TIMED_WAITING 線程sleep或者wait(long),
* LockSupport#parkNanos LockSupport.parkNanos, 等待指定時間後繼續執行
* TERMINATED 線程執行完畢, 已經被終止
* @throws BrokenBarrierException
* @throws InterruptedException
*/
@Test
public void testThreadStatus() throws BrokenBarrierException, InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
Thread thread = new Thread(() -> {
try {
LockSupport.park(this);
TimeUnit.SECONDS.sleep(5);
synchronized (ThreadTest.class) {
// 嘗試獲取鎖, 獲取不到進入Blocked狀態等待鎖
}
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
});
log.info("線程創建還未啓動, 狀態爲: {}", thread.getState());
thread.start();
log.info("線程已經啓動, 狀態爲: {}", thread.getState());
TimeUnit.SECONDS.sleep(1);
log.info("線程已經park, 狀態爲: {}", thread.getState());
LockSupport.unpark(thread);
TimeUnit.SECONDS.sleep(3);
log.info("線程啓動後進入sleep狀態, 狀態爲: {}", thread.getState());
synchronized (ThreadTest.class) {
// 前面sleep了4秒, 5到7秒main線程持有鎖.
// 子線程第6秒開始獲取鎖, 但需要等待main線程釋放鎖, 此時子線程是BLOCKED狀態
TimeUnit.SECONDS.sleep(3);
log.info("線程等待鎖, 狀態爲: {}", thread.getState());
}
cyclicBarrier.await();
log.info("線程已經執行結束, 狀態爲: {}", thread.getState());
}
執行結果爲
- 線程創建還未啓動, 狀態爲: NEW
- 線程已經啓動, 狀態爲: RUNNABLE
- 線程已經park, 狀態爲: WAITING
- 線程啓動後進入sleep狀態, 狀態爲: TIMED_WAITING
- 線程等待鎖, 狀態爲: BLOCKED
- 線程已經執行結束, 狀態爲: TERMINATED
線程狀態的轉換
下面我們避免繁瑣的流程圖, 簡單描述下下線程狀態的相互轉換.
- new可以轉換爲runnable
- runnable可能轉換爲blocked或waiting, timed_waiting中的一種
- blocked或waiting, timed_waiting狀態在條件滿足後可以轉換爲runnable
- 線程執行完畢後, 進入terminated狀態