通用的線程生命周期
通用的線程生命周期基本上可以用下圖這個(gè)“五態(tài)模型”來(lái)描述侍瑟。這五態(tài)分別是:初始狀 態(tài)、可運(yùn)行狀態(tài)莱预、運(yùn)行狀態(tài)柠掂、休眠狀態(tài)和終止?fàn)顟B(tài)。
這“五態(tài)模型”的詳細(xì)情況如下所示依沮。
- 初始狀態(tài)涯贞,指的是線程已經(jīng)被創(chuàng)建,但是還不允許分配 CPU 執(zhí)行危喉。這個(gè)狀態(tài)屬于編程語(yǔ) 言特有的宋渔,不過(guò)這里所謂的被創(chuàng)建,僅僅是在編程語(yǔ)言層面被創(chuàng)建辜限,而在操作系統(tǒng)層 面皇拣,真正的線程還沒(méi)有創(chuàng)建。
- 可運(yùn)行狀態(tài)列粪,指的是線程可以分配 CPU 執(zhí)行审磁。在這種狀態(tài)下,真正的操作系統(tǒng)線程已經(jīng) 被成功創(chuàng)建了岂座,所以可以分配 CPU 執(zhí)行态蒂。
- 當(dāng)有空閑的 CPU 時(shí),操作系統(tǒng)會(huì)將其分配給一個(gè)處于可運(yùn)行狀態(tài)的線程费什,被分配到 CPU 的線程的狀態(tài)就轉(zhuǎn)換成了運(yùn)行狀態(tài)钾恢。
- 運(yùn)行狀態(tài)的線程如果調(diào)用一個(gè)阻塞的 API(例如以阻塞方式讀文件)或者等待某個(gè)事件 (例如條件變量),那么線程的狀態(tài)就會(huì)轉(zhuǎn)換到休眠狀態(tài)鸳址,同時(shí)釋放 CPU 使用權(quán)瘩蚪,休眠 狀態(tài)的線程永遠(yuǎn)沒(méi)有機(jī)會(huì)獲得 CPU 使用權(quán)。當(dāng)?shù)却氖录霈F(xiàn)了稿黍,線程就會(huì)從休眠狀態(tài) 轉(zhuǎn)換到可運(yùn)行狀態(tài)疹瘦。
- 線程執(zhí)行完或者出現(xiàn)異常就會(huì)進(jìn)入終止?fàn)顟B(tài),終止?fàn)顟B(tài)的線程不會(huì)切換到其他任何狀 態(tài)巡球,進(jìn)入終止?fàn)顟B(tài)也就意味著線程的生命周期結(jié)束了言沐。
這五種狀態(tài)在不同編程語(yǔ)言里會(huì)有簡(jiǎn)化合并。例如酣栈,C 語(yǔ)言的 POSIX Threads 規(guī)范险胰,就把 初始狀態(tài)和可運(yùn)行狀態(tài)合并了;Java 語(yǔ)言里則把可運(yùn)行狀態(tài)和運(yùn)行狀態(tài)合并了,這兩個(gè)狀 態(tài)在操作系統(tǒng)調(diào)度層面有用矿筝,而 JVM 層面不關(guān)心這兩個(gè)狀態(tài)起便,因?yàn)?JVM 把線程調(diào)度交給 操作系統(tǒng)處理了。
除了簡(jiǎn)化合并,這五種狀態(tài)也有可能被細(xì)化榆综。
Java 中線程的生命周期
Java 語(yǔ)言中線程共有六種狀態(tài)妙痹,分別是:
- NEW(初始化狀態(tài))
- RUNNABLE(可運(yùn)行 / 運(yùn)行狀態(tài))
- BLOCKED(阻塞狀態(tài))
- WAITING(無(wú)時(shí)限等待)
- TIMED_WAITING(有時(shí)限等待)
- TERMINATED(終止?fàn)顟B(tài))
這看上去挺復(fù)雜的,狀態(tài)類(lèi)型也比較多奖年。但其實(shí)在操作系統(tǒng)層面细诸,Java 線程中的 BLOCKED、WAITING陋守、TIMED_WAITING 是一種狀態(tài),即休眠狀態(tài)利赋。也就是說(shuō)只要 Java 線程處于這三種狀態(tài)之一水评,那么這個(gè)線程就永遠(yuǎn)沒(méi)有 CPU 的使用權(quán)。
線程狀態(tài)的切換
1. RUNNABLE 與 BLOCKED 的狀態(tài)轉(zhuǎn)換
只有一種場(chǎng)景會(huì)觸發(fā)這種轉(zhuǎn)換媚送,就是線程等待 synchronized 的隱式鎖中燥。synchronized 修 飾的方法、代碼塊同一時(shí)刻只允許一個(gè)線程執(zhí)行塘偎,其他線程只能等待疗涉,這種情況下,等待的 線程就會(huì)從 RUNNABLE 轉(zhuǎn)換到 BLOCKED 狀態(tài)吟秩。而當(dāng)?shù)却木€程獲得 synchronized 隱 式鎖時(shí)咱扣,就又會(huì)從 BLOCKED 轉(zhuǎn)換到 RUNNABLE 狀態(tài)。而我們平時(shí)所謂的 Java 在調(diào)用阻塞式 API 時(shí)涵防,線程會(huì)阻塞闹伪,指的是操作系統(tǒng)線程的狀態(tài), 并不是Java 線程的狀態(tài)壮池,Java 線程的狀態(tài)會(huì)依然保持 RUNNABLE 狀態(tài)偏瓤。
2. RUNNABLE 與 WAITING 的狀態(tài)轉(zhuǎn)換 總體來(lái)說(shuō),有三種場(chǎng)景會(huì)觸發(fā)這種轉(zhuǎn)換椰憋。
第一種場(chǎng)景厅克,獲得 synchronized 隱式鎖的線程,調(diào)用無(wú)參數(shù)的 Object.wait() 方法橙依。
第二種場(chǎng)景证舟,調(diào)用無(wú)參數(shù)的 Thread.join() 方法。
第三種場(chǎng)景票编,調(diào)用 LockSupport.park() 方法褪储。其中的 LockSupport 對(duì)象,其實(shí) Java 并發(fā)包中的鎖慧域,都是基于它實(shí)現(xiàn)的鲤竹。調(diào)用 LockSupport.park() 方法,當(dāng)前 線程會(huì)阻塞,線程的狀態(tài)會(huì)從 RUNNABLE 轉(zhuǎn)換到 WAITING辛藻。調(diào)用 LockSupport.unpark(Thread thread) 可喚醒目標(biāo)線程碘橘,目標(biāo)線程的狀態(tài)又會(huì)從 WAITING 狀態(tài)轉(zhuǎn)換到 RUNNABLE。
3. RUNNABLE 與 TIMED_WAITING 的狀態(tài)轉(zhuǎn)換 有五種場(chǎng)景會(huì)觸發(fā)這種轉(zhuǎn)換:
- 調(diào)用帶超時(shí)參數(shù)的 Thread.sleep(long millis) 方法;
- 獲得 synchronized 隱式鎖的線程吱肌,調(diào)用帶超時(shí)參數(shù)的 Object.wait(long timeout) 方
法; - 調(diào)用帶超時(shí)參數(shù)的 Thread.join(long millis) 方法;
- 調(diào)用帶超時(shí)參數(shù)的 LockSupport.parkNanos(Object blocker, long deadline) 方法;
- 調(diào)用帶超時(shí)參數(shù)的 LockSupport.parkUntil(long deadline) 方法痘拆。
這里你會(huì)發(fā)現(xiàn) TIMED_WAITING 和 WAITING 狀態(tài)的區(qū)別,僅僅是觸發(fā)條件多了超時(shí)參 數(shù)氮墨。
4. 從 NEW 到 RUNNABLE 狀態(tài)
Java 剛創(chuàng)建出來(lái)的 Thread 對(duì)象就是 NEW 狀態(tài)纺蛆,而創(chuàng)建 Thread 對(duì)象主要有兩種方法。 一種是繼承 Thread 對(duì)象规揪,重寫(xiě) run() 方法桥氏。
- 從 RUNNABLE 到 TERMINATED 狀態(tài)
線程執(zhí)行完 run() 方法后,會(huì)自動(dòng)轉(zhuǎn)換到 TERMINATED 狀態(tài)猛铅。