線程是比進程更加輕量級的調度執行單位,理解線程是理解併發編程的不可或缺的一部分;而生產過程當中不可能永遠使用裸線程,須要線程池技術,線程池是管理和調度線程的資源池。由於前不久遇到了一個關於線程狀態的問題,今天就趁熱打鐵從源碼的層面來談一談線程和線程池的狀態及狀態之間的轉移。編程
JDK中,線程(Thread)定義了6種狀態: NEW(新建)、RUNNABLE(可執行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(限時等待)、TERMINATED(結束)。緩存
源碼以下:併發
/** * A thread state. A thread can be in one of the following states: * <ul> * <li>{@link #NEW} * A thread that has not yet started is in this state. * </li> * <li>{@link #RUNNABLE} * A thread executing in the Java virtual machine is in this state. * </li> * <li>{@link #BLOCKED} * A thread that is blocked waiting for a monitor lock * is in this state. * </li> * <li>{@link #WAITING} * A thread that is waiting indefinitely for another thread to * perform a particular action is in this state. * </li> * <li>{@link #TIMED_WAITING} * A thread that is waiting for another thread to perform an action * for up to a specified waiting time is in this state. * </li> * <li>{@link #TERMINATED} * A thread that has exited is in this state. * </li> * </ul> * * <p> * A thread can be in only one state at a given point in time. * These states are virtual machine states which do not reflect * any operating system thread states. * * @since 1.5 * @see #getState */ public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <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> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <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, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
線程在一個給定的時間點只能處於下面其中一種狀態:源碼分析
這些狀態是虛擬機狀態,並不能反映任何操做系統的線程狀態。this
NEW:線程還沒有啓動的線程狀態。當在程序中建立一個線程的時候Thread t = new Thread(Runnable);,線程處於NEW狀態。spa
RUNNABLE:可運行線程的線程狀態。處於可運行狀態的線程正在Java虛擬機中執行,但它可能正在等待操做系統中的其餘資源,好比處理器。也就是說, 這個狀態就是可運行可不運行的狀態。注意Runnable ≠ Running。操作系統
BLOCKED:等待監視器鎖的阻塞線程的線程狀態。好比,線程試圖經過synchronized去獲取某個鎖,可是其餘線程已經獨佔了,那麼當前線程就會處於阻塞狀態。處於阻塞狀態的線程正在等待監視器鎖去進入同步塊/方法(等待一個監視器鎖時爲了進入同步塊/方法),或者在調用Object.wait()後從新進入同步塊/方法。線程
WAITING:調用如下方法之一,線程會處於等待狀態:翻譯
其實wait()方法有多重形式,能夠不帶參數,能夠帶參數,參數表示等待時間(單位ms),如圖所示:code
「BLOCKED(阻塞狀態)」和「WAITING(等待狀態)」的區別:阻塞狀態在等待獲取一個排它鎖,這個事件將會在另一個線程放棄這個鎖的時候發生,而後由阻塞狀態變爲可執行狀態;而等待狀態則是在等待一段時間,或者等待喚醒動做的發生。
TIMED_WAITING:一個線程調用瞭如下方法之一(方法須要帶具體的等待時間),會處於定時等待狀態:
TERMINATED: 該線程已經執行完畢。
其實這些大部分在源碼的註釋中能夠找到。下面我本身翻譯的中文版,不嫌棄的話能夠參考:
/** * 線程狀態。 一個線程能夠處於下列狀態之一: * * NEW:還沒有啓動的線程處於這個狀態。 * * RUNNABLE:正在Java虛擬機中執行的線程處於這個狀態。 * * BLOCKED:阻塞中,等待監視器鎖的線程處於這個狀態。 * * WAITING:無限期等待另外一個線程執行特定操做的線程處於這種狀態。 * * TIMED_WAITING:正在等待另外一個線程執行某個操做的線程在指定的等待時間內處於這種狀態。 * * TERMINATED:已經退出的線程處於這個狀態。 * * 線程在一個給定的時間點只能處於一種狀態。 * 這些狀態是虛擬機狀態,並不能反映任何操做系統的線程狀態。 * */ public enum State { /** * 線程還沒有啓動的線程狀態。 */ NEW, /** * 可運行線程的線程狀態。處於可運行狀態的線程正在Java虛擬機中執行, * 但它可能正在等待操做系統中的其餘資源,好比處理器。 */ RUNNABLE, /** * 等待監視器鎖的阻塞線程的線程狀態。 * 處於阻塞狀態的線程正在等待監視器鎖進入同步塊/方法, * 或者在調用Object.wait後從新進入同步塊/方法。 */ BLOCKED, /** * 等待線程的線程狀態。 * 調用如下方法之一,線程會處於等待狀態: * Object.wait()注意:括號內不帶參數; * Thread.join()注意:擴號內不帶參數; * LockSupport.park(); * * 處於等待狀態的線程正在等待另一個線程執行特定的操做。 * * 例如,一個調用了object.wait()方法的線程正在等待另一下線程調用 * object.notify()或者object.notifyAll()方法。注意,這兩個object是同一個object。 * 一個調用了Thread.join()方法的線程正在等待一個特定的線程去終止。 */ WAITING, /** * 具備指定等待時間的等待線程的線程狀態。 * 一個線程調用瞭如下方法之一(方法須要帶具體的等待時間),會處於定時等待狀態: * Thread.sleep(timeout) * Object.wait(timeout) * Thread.join(timeout) * LockSupport.parkNanos() * LockSupport.parkUntil() */ TIMED_WAITING, /** * 終止的線程狀態。 * 該線程已經執行完畢。 */ TERMINATED; }
狀態轉移圖如圖所示:
在生產環境中,爲每一個任務分配一個線程是存在缺陷的,例如資源消耗和穩定性等,因此須要使用線程池。
Java類庫提供了靈活的線程池,能夠調用Executors中的靜態工廠方法建立線程池。如
無論是newFixedThreadPool仍是newCachedThreadPool,底層都是經過ThreadPoolExecutor實現的,本文只談ThreadPoolExecutor的狀態。
在JDK源碼中,線程池(ThreadPoolExecutor)定義了五種狀態:RUNNING、SHUTDOWN、STOP、TIDYING和TERMINATED。
源碼以下:
private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS;
RUNNING — 運行狀態,能夠添加新任務,也能夠處理阻塞隊列中的任務。
SHUTDOWN — 待關閉狀態,再也不接受新的任務,會繼續處理阻塞隊列中的任務。
STOP — 中止狀態,再也不接受新的任務,不會執行阻塞隊列中的任務,打斷正在執行的任務。
TIDYING — 整理狀態,全部任務都處理完畢,workerCount爲0,線程轉到該狀態將會運行terminated()鉤子方法。
TERMINATED — 終止狀態,terminated()方法執行完畢。
* RUNNING -> SHUTDOWN * On invocation of shutdown(), perhaps implicitly in finalize() * (RUNNING or SHUTDOWN) -> STOP * On invocation of shutdownNow() * SHUTDOWN -> TIDYING * When both queue and pool are empty * STOP -> TIDYING * When pool is empty * TIDYING -> TERMINATED * When the terminated() hook method has completed
線程池的初始化狀態是RUNNING。換句話說,線程池被一旦被建立,就處於RUNNING狀態,而且線程池中的任務數爲0。
當線程池處於RUNNING狀態時,調用shutdown()方法,線程池RUNNING狀態轉爲SHUTDOWN狀態。
當線程池處於RUNNING or SHUTDOWN時,調用shutdownNow()方法時,線程池由(RUNNING or SHUTDOWN )狀態轉爲STOP狀態。
當線程池在SHUTDOWN狀態下,阻塞隊列爲空而且線程池中執行的任務也爲空時,就會由 SHUTDOWN狀態轉爲TIDYING狀態。
當線程池處於STOP狀態,當線程池中執行的任務爲空的時候,線程池有STOP狀態轉爲TIDYING狀態。
當線程池處於TIDYING狀態,當執行完terminated()以後,就會由TIDYING狀態轉爲TERMINATED狀態。
狀態轉移圖如圖所示:
理解線程和線程池對於咱們平常開發或者診斷分析,都是不可或缺的基礎。本文從源碼分析了線程和線程池的狀態和各類方法之間的對應關係,但願對你們有幫助,文中若是有地方不妥還請你們指正。