這是大廠面試系列,還原真實場景,把問題講清楚。
點贊再看,養成習慣~ 微信搜索【武哥聊編程】,關注這個 Java 菜鳥。
是的,今天必須要把它給徹底根治了!
現在 Java 面試,基本上都會問到多線程,那麼隨之而來的線程狀態,很多時候都會被問到,大部分人都知道線程的幾種狀態,但是讓你整體全部串起來,做到面試時遊刃有餘,估計很多人記憶不是很清晰。
今天武哥就把這些全部給整了,下次面試官再問,就把這篇文章扔給他。
1. 開局一張圖,其他全靠吹
本文的核心就在於這幅圖。我用 PPT 畫了好幾個小時,應該是全網最清新最好看的一張圖了吧(不接受反駁,誰反駁上去也是一jio,手動滑稽)
好了,牛逼不能再吹了,我們根據上面這幅圖來展開,我把這塊涉及到的東西都跟大家理一遍,希望看過這篇文章的小夥伴,特別是初學者,後面不用再在這個問題上糾結了。
針對線程的狀態,首先來看下官方的源碼(除去了註釋):
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
所以從官方的角度,線程是有6中狀態的,結合官方的註釋,我首先把六種狀態解釋一下,然後具體講解一下每個狀態之間轉換所涉及到的那些方法。把這些都理清楚了,這一塊也就差不多了。
2. 線程的6種狀態解釋
結合上面的那張圖一起看:
NEW:線程被創建出來了,但是還沒有start()
。
RUNNABLE:可運行狀態,這個狀態比較特殊,我在圖中把這個狀態拆分成了兩部分:一部分是READY
,顧名思義是準備狀態,另一部分是RUNNING
,即運行狀態。
準備狀態:只能說明你有資格運行,單隻要調度程序沒有調度到你,你就永遠是準備狀態。從圖中可以看出,好幾個狀態都能切換到準備狀態,至於切換的方法,我在下文詳細給大家整理出來介紹。
運行狀態:線程調度程序從可運行池中選擇一個線程作爲當前線程時線程所處的狀態。這也是線程進入運行狀態的唯一一種方式。說白了,就是線程跑起來了。
BLOCKED:阻塞狀態是線程阻塞在進入synchronized
關鍵字修飾的方法或代碼塊(獲取鎖)時的狀態。
WAITING:等待狀態的線程不會被分配 CPU 執行時間,它們要等待被顯式地喚醒,否則會處於無限期等待的狀態。
TIMED_WAITING:超時等待狀態的線程不會被分配 CPU 執行時間,不過無須無限期等待被其他線程顯示地喚醒,在達到一定時間後它們會自動喚醒。這是和上面的 WAITING 狀態的區別。
TERMINATED:終止狀態,顧名思義,線程執行結束了。
3. 線程狀態之間的切換函數分析
從上面的圖中可以看出,線程之間的狀態切換,主要涉及到以下幾個函數:
Thread.sleep
、Thread.yeild
、Object.wait
、Thread.join
、Object.notify/notifyAll
、LockSupport.park/unpark/parkNanos/parkUtil
接下來針對這幾個函數,做一下簡單的功能分析和對比,大家可以結合上面的那幅圖。
Thread.sleep(time)
:當前線程調用此方法,顧名思義,就是讓當前線程進入 TIMED_WAITING 狀態,睡眠固定的時間(但是不釋放對象鎖),到點後自動喚醒,進入準備狀態。主要作用是給其他線程執行機會。
Thread.yield()
:當前線程調用此方法,放棄獲取的CPU時間片,但不釋放鎖資源,由運行狀態變爲準備狀態,讓OS再次選擇線程。實際中無法保證yield()
達到讓步目的,因爲讓步的線程還有可能被線程調度程序再次選中。它跟 sleep
方法的區別在於不能指定暫停多少時間。
Object.wait()
:當前線程調用對象的wait()
方法,當前線程釋放對象鎖,進入WAITING 狀態。依靠notify()/notifyAll()
來喚醒;而wait(time)
方法的主要區別是進入 TIMED_WAITING 狀態,到達時間後,自動喚醒。
Object.notify/notifyAll
:notify()
喚醒在此對象監視器上等待的單個線程,選擇是任意性的。notifyAll()
喚醒在此對象監視器上等待的所有線程。
Thread.join()
:當前線程裏調用其它線程 t 的join()
方法,當前線程進入 WAITING 狀態,當前線程不會釋放已經持有的對象鎖。線程 t 執行完畢,當前線程進入 RUNNABLE 狀態。而join(time)
方法的主要區別是當前線程進入 TIMED_WAITING 狀態,到達時間後,進入 RUNNABLE 狀態。
LockSupport.park()
:當前線程進入 WAITING 狀態,需要通過LockSupport.unpark(thread)
來喚醒。
LockSupport.parkNanos(nanos)/parkUntil(deadlines)
:當前線程進入 TIMED_WAITING 狀態,需要通過``LockSupport.unpark(thread)`來喚醒。
相比與wait
方法,LockSupport
不需要獲得鎖就可以讓線程進入 WAITING 或者 TIMED_WAITING 狀態。
當然了,還有個 BLOCKED 狀態,涉及到 synchronized
關鍵字,由於這塊也是面試經常會問到的,下一篇我會全面剖析一下synchronized
。
關於線程的狀態以及狀態間切換所涉及到的函數,這篇文章就總結這麼多。全篇圍繞文章開頭的那幅圖,我覺得這塊內容,只要把那幅圖記住,就基本沒什麼問題了。如有疑問,歡迎討論。
如果覺得有幫助,希望老鐵們來個三連擊,給更多的人看到這篇文章
1、關注我的原創微信公衆號「武哥聊編程」,專注於Java、數據結構和算法、微服務、中間件等技術分享,保證你看完有所收穫。
2、給俺點個贊唄,可以讓更多的人看到這篇文章,順便激勵下我繼續寫作,嘻嘻。
作者info
【作者】:武哥
【公衆號】:武哥聊編程。歡迎大家關注~
【作者簡介】:同濟大學,碩士。先後在華爲、科大訊飛、拼多多采坑。一個自學 Java 的菜鳥,期待你的關注。
點贊是對我最大的鼓勵
↓↓↓↓↓↓