10句有效代碼帶你分析6種線程狀態

原創文章, 轉載請私信. 關注 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

線程狀態的轉換

下面我們避免繁瑣的流程圖, 簡單描述下下線程狀態的相互轉換.

  1. new可以轉換爲runnable
  2. runnable可能轉換爲blocked或waiting, timed_waiting中的一種
  3. blocked或waiting, timed_waiting狀態在條件滿足後可以轉換爲runnable
  4. 線程執行完畢後, 進入terminated狀態
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章