Java多線(xiàn)程之線(xiàn)程狀態(tài)的遷移詳解
一、六種狀態(tài)
java.lang.Thread 的狀態(tài)分為以下 6 種,它們以枚舉的形式,封裝在了Thread類(lèi)內(nèi)部:
NEW:表示線(xiàn)程剛剛創(chuàng)建出來(lái),還未啟動(dòng)
RUNNABLE:可運(yùn)行狀態(tài),該狀態(tài)的線(xiàn)程可以是ready或running,唯一的決定因素是線(xiàn)程調(diào)度器
BLOCKED:阻塞,線(xiàn)程正在等待一個(gè)monitor鎖以便進(jìn)入一個(gè)同步代碼塊
WAITING:等待,一種掛起等待的狀態(tài)。一個(gè)線(xiàn)程處于waiting是為了等待其他線(xiàn)程執(zhí)行某個(gè)特定的動(dòng)作。
TIMED_WAITING:定時(shí)等待。
TERMINATED:終結(jié),線(xiàn)程執(zhí)行結(jié)束后的狀態(tài)。

二、狀態(tài)遷移圖

線(xiàn)程遷移圖網(wǎng)上有很多,這是我自己參考著繪制的一張。
線(xiàn)程遷移圖雖然是背了忘忘了背,反反復(fù)復(fù)很多遍,但是記憶這張圖其實(shí)并不困難。首先就是NEW和TERMINATED狀態(tài),一個(gè)表示剛剛創(chuàng)建,一個(gè)表示任務(wù)結(jié)束。
最重要的是記住WAITING和BLOCKED這兩種狀態(tài)與RUNNABLE相互切換的條件。
BLOCKED狀態(tài)在Java doc中的描述是“等待一個(gè)monitor鎖”,monitor對(duì)象是與對(duì)象實(shí)例相關(guān)聯(lián)的一個(gè)鎖對(duì)象,這個(gè)鎖對(duì)象實(shí)際上就是 synchronized 的具體實(shí)現(xiàn),一般稱(chēng)之為重量級(jí)鎖,進(jìn)入同步代碼塊的過(guò)程,實(shí)際上就是獲取到 monitor 對(duì)象的鎖的過(guò)程。如果鎖被其他線(xiàn)程占用,當(dāng)前線(xiàn)程就變成了BLOCKED狀態(tài),如果得到了鎖,就由BLOCKED切換到RUNNABLE狀態(tài)。
WAITING 是一種掛起狀態(tài),處于 waiting 的線(xiàn)程表示它正在等待一個(gè)有緣人~ 這個(gè)有緣人需要執(zhí)行特定的動(dòng)作才能解救 waiting 中的線(xiàn)程。就像孫悟空在五指山下等了五百年,只有玄奘摘下山頂?shù)姆洳拍軌蚱仆炼觥?/p>
導(dǎo)致 WAITING 的情況只有三種:
wait()
join()
LockSupport.park()
wait() 方法是 Object 的成員方法,它可以令當(dāng)前線(xiàn)程針對(duì)于某個(gè)對(duì)象掛起等待,并釋放獲得的鎖資源,只有當(dāng)其他線(xiàn)程調(diào)用這個(gè)對(duì)象的notify()或 notifyAll() 方法,才能夠喚醒等待中的線(xiàn)程。注意,notify()或 notifyAll() 不會(huì)釋放鎖資源。
join() 方法是線(xiàn)程的一個(gè)成員方法,“加入一個(gè)線(xiàn)程”,這好像合情合理,比如 t1線(xiàn)程在執(zhí)行的過(guò)程中調(diào)用了 t2.join(),那么好吧, t1就會(huì)由 RUNNABLE 變?yōu)?WAITING,因?yàn)樗却?t2 執(zhí)行完后才會(huì)繼續(xù)執(zhí)行,說(shuō)白了,就是方便程序員讓線(xiàn)程插隊(duì)用的:

LockSupport.park()更方便,它是一個(gè)靜態(tài)方法,可以讓線(xiàn)程在調(diào)用的位置直接WAITING,然后在其他線(xiàn)程中,獲取到WAITING中的線(xiàn)程對(duì)象,傳入LockSupport(thread) 直接恢復(fù)運(yùn)行。
三、線(xiàn)程狀態(tài)模擬
準(zhǔn)備三個(gè)線(xiàn)程 monitor 監(jiān)視線(xiàn)程,主要實(shí)時(shí)監(jiān)視 t1線(xiàn)程的狀態(tài);
t1 線(xiàn)程模擬各種狀態(tài),t2 輔助 t1 模擬各種狀態(tài):
public class ThreadState {
static Object lock = new Object();
// 模擬 NEW、RUNNABLE、WAITING、TIMED_WAITING、BLOCKED、TERMINATED
public static void main(String[] args) {
ThreadState thisObj = new ThreadState();
Thread t1 = new Thread(() -> {
try {
// 先獲取 t2 對(duì)象
Thread t2 = getThreadByName("t2");
// 先執(zhí)行一套邏輯,推遲同步代碼塊的調(diào)用
String str = "";
for (int i = 0; i < 10000; i++) {
str += i;
}
// 調(diào)用同步代碼塊
thisObj.doSync();
// t2準(zhǔn)備插隊(duì)
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1");
System.out.println("t1 剛創(chuàng)建:" + t1.getState());
Thread t2 = new Thread(() -> {
try {
// 直接獲取同步鎖
thisObj.doSync();
// 釋放鎖后在運(yùn)行一段時(shí)間
TimeUnit.SECONDS.sleep(30);
} catch (Exception e) {
e.printStackTrace();
}
}, "t2");
Thread monitor = new Thread(() -> {
Thread.State t1State = null;
while (true) {
if (!t1.getState().equals(t1State)) {
t1State = t1.getState();
System.out.println("t1 此刻狀態(tài):" + t1.getState());
}
if (t1State.equals(Thread.State.TERMINATED))
break;
}
}, "monitor");
monitor.start();
t2.start();
t1.start();
}
public synchronized void doSync() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
String str = "";
for (int i = 0; i < 100000; i++) {
str += i;
}
}
public static Thread getThreadByName(String name) {
Optional<Thread> first = Thread.getAllStackTraces()
.keySet()
.stream()
.filter(thread -> thread.getName().equals(name))
.findFirst();
return first.get();
}
}
輸出:
t1 剛創(chuàng)建:NEW
t1 此刻狀態(tài):RUNNABLE
t1 此刻狀態(tài):BLOCKED
t1 此刻狀態(tài):TIMED_WAITING
t1 此刻狀態(tài):TIMED_WAITING
t1 此刻狀態(tài):RUNNABLE
t1 此刻狀態(tài):WAITING
t1 此刻狀態(tài):BLOCKED
t1 此刻狀態(tài):TERMINATED
總結(jié)
線(xiàn)程狀態(tài)遷移是非常重要的多線(xiàn)程基礎(chǔ)知識(shí),在調(diào)試多線(xiàn)程問(wèn)題的時(shí)候,能夠發(fā)揮很大的作用。
6 種狀態(tài)不僅要熟記,而且在什么情況下會(huì)出現(xiàn)這些狀態(tài)也要清晰明了。
如果條件允許,可以試著通過(guò)不同的方法來(lái)模擬線(xiàn)程的六種狀態(tài)的切換,可以加深對(duì)線(xiàn)程生命周期的理解。
到此這篇關(guān)于Java多線(xiàn)程之線(xiàn)程狀態(tài)的遷移詳解的文章就介紹到這了,更多相關(guān)java線(xiàn)程狀態(tài)遷移內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項(xiàng)目更換項(xiàng)目名稱(chēng)的實(shí)現(xiàn)
本文主要介紹了SpringBoot項(xiàng)目更換項(xiàng)目名稱(chēng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
教你用Java在個(gè)人電腦上實(shí)現(xiàn)微信掃碼支付
今天給大家?guī)?lái)的是Java實(shí)戰(zhàn)的相關(guān)知識(shí),文章圍繞著Java在個(gè)人電腦上實(shí)現(xiàn)微信掃碼支付展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
Java與C++實(shí)現(xiàn)相同的MD5加密算法簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇Java與C++實(shí)現(xiàn)相同的MD5加密算法簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09
將Java對(duì)象序列化成JSON和XML格式的實(shí)例
下面小編就為大家分享一篇將Java對(duì)象序列化成JSON和XML格式的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
Spring Cloud下實(shí)現(xiàn)用戶(hù)鑒權(quán)的方案
Java下常用的安全框架主要有Spring Security和shiro,都可提供非常強(qiáng)大的功能,但學(xué)習(xí)成本較高。但在微服務(wù)下鑒權(quán)又會(huì)對(duì)服務(wù)有一定的入侵性。 因此,本文將介紹Spring Cloud下實(shí)現(xiàn)用戶(hù)鑒權(quán)的方案,感興趣的同學(xué)可以關(guān)注一下2021-11-11
SVN導(dǎo)入maven項(xiàng)目報(bào)錯(cuò)解決方案
這篇文章主要介紹了SVN導(dǎo)入maven項(xiàng)目報(bào)錯(cuò)解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12

