天天看點

從源碼解讀線程(Thread)和線程池(ThreadPoolExecutor)的狀态

線程是比程序更加輕量級的排程執行機關,了解線程是了解并發程式設計的不可或缺的一部分;而生産過程中不可能永遠使用裸線程,需要線程池技術,線程池是管理和排程線程的資源池。因為前不久遇到了一個關于線程狀态的問題,今天就趁熱打鐵從源碼的層面來談一談線程和線程池的狀态及狀态之間的轉移。

JDK中,線程(Thread)定義了6種狀态:  NEW(建立)、RUNNABLE(可執行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(限時等待)、TERMINATED(結束)。

源碼如下:

線程在一個給定的時間點隻能處于下面其中一種狀态:

這些狀态是虛拟機狀态,并不能反映任何作業系統的線程狀态。

NEW:尚未啟動的線程處于這個狀态。Thread thread = new Thread(new Runnable(){...});處于這個狀态。

RUNNABLE:可運作的線程處于這個狀态。對應作業系統中的兩種狀态:ready和running,也就是說RUNNABLE狀态既可以是可運作的,也可以是實際運作中的,有可能正在執行,也有可能沒有正在執行。關于這個問題的了解,可以對比想一下,thread.start()調用之後線程會立刻執行嗎?

BLOCKED:阻塞,進入synchronized修飾的方法或者代碼塊,等待螢幕鎖的線程處于這個狀态。

WAITING:無限期等待另一個線程執行特定操作的線程處于這種狀态。

TIMED_WAITING:正在等待另一個線程執行某個操作的線程在指定的等待時間内處于這種狀态。

TERMINATED:已經退出的線程處于這個狀态。

NEW:線程尚未啟動的線程狀态。當在程式中建立一個線程的時候Thread t = new Thread(Runnable);,線程處于NEW狀态。

RUNNABLE:可運作線程的線程狀态。處于可運作狀态的線程正在Java虛拟機中執行,但它可能正在等待作業系統中的其他資源,比如處理器。也就是說, 這個狀态就是可運作也可不運作的狀态。注意Runnable ≠ Running。

BLOCKED:進入synchronized修飾的方法或者代碼塊,等待螢幕鎖的阻塞線程的線程狀态。比如,線程試圖通過synchronized去擷取螢幕鎖,但是其他線程已經獨占了,那麼目前線程就會處于阻塞狀态。等到獲得了螢幕鎖之後會再次進入RUNNABLE狀态。

WAITING:調用以下方法之一,線程會處于等待狀态:

Object.wait()注意:括号内不帶參數;

 Thread.join()注意:擴号内不帶參數;

 LockSupport.park();

其實wait()方法有多重形式,可以不帶參數,可以帶參數,參數表示等待時間(機關ms),如圖所示:

“BLOCKED(阻塞狀态)”和“WAITING(等待狀态)”的差別:阻塞狀态在等待擷取一個排它鎖,這個事件将會在另外一個線程放棄這個鎖的時候發生,然後由阻塞狀态變為可執行狀态;而等待狀态則是在等待一段時間,或者等待喚醒動作的發生。

TIMED_WAITING:一個線程調用了以下方法之一(方法需要帶具體的等待時間),會處于定時等待狀态:

Thread.sleep(long timeout)

Object.wait(long timeout)

Thread.join(long timeout)

LockSupport.parkNanos()

LockSupport.parkUntil()

TERMINATED:   該線程已經執行完畢。執行完畢指的是線程正常執行完了run方法之後退出,也可以是遇到了未捕獲的異常而退出。

其實這些大部分在源碼的注釋中可以找到。下面我自己翻譯的中文版,不嫌棄的話可以參考:

狀态轉移圖如圖所示:

在生産環境中,為每個任務配置設定一個線程是存在缺陷的,例如資源消耗和穩定性等,是以需要使用線程池。

Java類庫提供了靈活的線程池,可以調用Executors中的靜态工廠方法建立線程池。如

newFixedThreadPool:固定長度的線程池

newCachedThreadPool:可緩存的線程池。

不管是newFixedThreadPool還是newCachedThreadPool,底層都是通過ThreadPoolExecutor實作的,本文隻談ThreadPoolExecutor的狀态。

在JDK源碼中,線程池(ThreadPoolExecutor)定義了五種狀态:RUNNING、SHUTDOWN、STOP、TIDYING和TERMINATED。

RUNNING — 運作狀态,可以添加新任務,也可以處理阻塞隊列中的任務。 

SHUTDOWN — 待關閉狀态,不再接受新的任務,會繼續處理阻塞隊列中的任務。 

STOP — 停止狀态,不再接受新的任務,不會執行阻塞隊列中的任務,打斷正在執行的任務。 

TIDYING — 整理狀态,所有任務都處理完畢,workerCount為0,線程轉到該狀态将會運作terminated()鈎子方法。

TERMINATED — 終止狀态,terminated()方法執行完畢。

線程池的初始化狀态是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狀态。

了解線程和線程池對于我們日常開發或者診斷分析,都是不可或缺的基礎。本文從源碼分析了線程和線程池的狀态和各種方法之間的對應關系,希望對大家有幫助,文中如果有地方不妥還請大家指正。

由于部落客也是在攀登的路上,文中可能存在不當之處,歡迎各位多指教! 如果文章對您有用,那麼請點個”推薦“,以資鼓勵!

繼續閱讀