大神告訴你|JAVA 線程狀態(tài)中可能會(huì)忽視的一些誤區(qū)

BLOCKED 和 WAITING 的區(qū)別

BLOCKED 和 WAITING 兩種狀態(tài)從結(jié)果上來(lái)看,都是線程暫停瓶殃,不會(huì)占用 CPU 資源,不過(guò)還是有一些區(qū)別的

BLOCKED

等待 Monitor 鎖的阻塞線程的線程狀態(tài)副签,處于阻塞狀態(tài)的線程正在等待 Monitor 鎖進(jìn)入 synchronized Block 或者 Method 遥椿,或者在調(diào)用 Object.wait 后重新進(jìn)入同步塊/方法。簡(jiǎn)單的說(shuō)淆储,就是線程等待 synchronized 形式的鎖時(shí)的狀態(tài)

下面這段代碼中冠场, t1 在等待 t0 的鎖釋放(synchronized代碼塊執(zhí)行完成),那么此時(shí) t1 的狀態(tài)就是 BLOCKED

Object lock = new Object();
Thread t0 = new Thread(new Runnable() {
    @Override
    public void run() {
        synchronized (lock){
            System.out.println("t0 acquire lock success");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
});
t0.start();
Thread.sleep(100);
Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        synchronized (lock){
            System.out.println("t1 acquire lock success");
        }
    }
});
t1.start();
Thread.sleep(100);
System.out.println("t0 state: "+t0.getState());
System.out.println("t1 state: "+t1.getState());
System.out.println("done.");

//output
t0 acquire lock success
t0 state: TIMED_WAITING
t1 state: BLOCKED
done.
t1 acquire lock success

WAITING

等待中的線程狀態(tài)本砰,下面幾個(gè)方法的調(diào)用會(huì)導(dǎo)致線程進(jìn)入 WAITING 狀態(tài):

Object.wait()
Thread.join()
LockSupport.park()
WAITING 狀態(tài)中的線程在等待其他線程執(zhí)行某些操作碴裙,比如在某個(gè)對(duì)象上調(diào)用 Object.wait() 的線程正在等待另一個(gè)線程在該對(duì)象上調(diào)用 Object.notify() 或 Object.notifyAll()。為 Thread.join() 的線程正在等待指定的線程停止点额。

下面這段代碼中舔株,t0 在通過(guò) synchronized 獲取了 lock 對(duì)象的鎖之后,進(jìn)行了 wait 操作还棱,導(dǎo)致 t0 進(jìn)入 WAITING 狀態(tài):

Object lock = new Object();
Thread t0 = new Thread(new Runnable() {
    @Override
    public void run() {
        synchronized (lock){
            System.out.println("t0 acquire lock success");
            try {
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
});
t0.start();
Thread.sleep(100);
System.out.println("t0 state: "+t0.getState());
System.out.println("done.");

//output
t0 acquire lock success
t0 state: WAITING
done.

區(qū)別

JAVA 中除了 synchronized Block/Method 的鎖载慈,還提供了 JUC 下的鎖實(shí)現(xiàn), juc.lock 下的鎖功能更強(qiáng)大珍手。比如支持中斷办铡,支持重入/非重入辞做,公平/非公平等;但是 juc 下的鎖和 synchronized 的實(shí)現(xiàn)可是不太一樣的

比如下面這段代碼寡具,同樣是等待鎖秤茅,可是和synchronized等待鎖的狀態(tài)還不一樣:

ReentrantLock reentrantLock = new ReentrantLock();
Thread t0 = new Thread(new Runnable() {
    @Override
    public void run() {
        reentrantLock.lock();

        System.out.println("t0 acquire lock success");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
t0.start();
Thread.sleep(100);
Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        reentrantLock.lock();
        System.out.println("t1 acquire lock success");
    }
});
t1.start();
Thread.sleep(100);
System.out.println("t0 state: "+t0.getState());
System.out.println("t1 state: "+t1.getState());
System.out.println("done.");

//output
t0 acquire lock success
t0 state: TIMED_WAITING
t1 state: WAITING
done.

同樣是加鎖,在 JUC 的鎖實(shí)現(xiàn)下線程狀態(tài)不太一樣童叠,所以在觀察線程狀態(tài)時(shí)框喳,不止是 BLOCKED 的狀態(tài)才是等待鎖, WAITING/TIMEWAITING 的狀態(tài)仍然可能是等待鎖的狀態(tài)

不過(guò) JUC 下的鎖實(shí)現(xiàn)拯钻,讓線程暫停/等待的核心方法還是 LockSupport.park 帖努, jstack 對(duì)于 PARKING 形式的 WAITING 會(huì)有標(biāo)注,所以在線程 stack 時(shí)還是能一眼看出來(lái)的:

//這里顯示了等待類(lèi)型
"Thread-0" #11 prio=5 os_prio=31 tid=0x00007f9308110000 nid=0x5c03 waiting on condition [0x0000700007fc3000]
   java.lang.Thread.State: WAITING (parking)//這里雖然是WAITING粪般,但還是標(biāo)注了是parking類(lèi)型的
        at sun.misc.Unsafe.park(Native Method)

而 synchronized 形式的鎖在 jstack 下的輸出會(huì)有所區(qū)別:

//這里顯示了等待類(lèi)型為monitor
"Thread-1" #12 prio=5 os_prio=31 tid=0x00007f833d919800 nid=0x5a03 waiting for monitor entry [0x00007000035af000]
   java.lang.Thread.State: BLOCKED (on object monitor)//這里是BLOCKED狀態(tài),同時(shí)顯示了monitor的歸屬

所以在觀察線程狀態(tài)時(shí)污桦,需要注意Object.wait()這種WAITING和juc下鎖導(dǎo)致的WAITING的區(qū)別

RUNNABLE 真的是 RUNNABLE 嗎亩歹?

下面是一段 jstack 輸出的例子,該線程現(xiàn)在正在執(zhí)行 socketRead0 方法(Native)凡橱,并且是 RUNNABLE 狀態(tài)

"RMI TCP Connection(2)-192.xxx.xx.xx" daemon prio=6 tid=0x000000000a3e8800 nid=0x158e50 runnable [0x000000000adbe000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.io.BufferedInputStream.fill(Unknown Source)
at java.io.BufferedInputStream.read(Unknown Source)
- locked (0x00000007ad784010) (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

但其實(shí)這里的 RUNNABLE 只是 JAVA 層面的線程狀態(tài)小作,在操作系統(tǒng)或進(jìn)程角度來(lái)看,該線程還是 WAITING 的狀態(tài)稼钩; SocketInputStream 是一個(gè) BIO 的實(shí)現(xiàn)顾稀,當(dāng)沒(méi)有收到數(shù)據(jù)(或者說(shuō)沒(méi)有準(zhǔn)備好可讀的數(shù)據(jù))時(shí)會(huì)發(fā)生阻塞,可這個(gè)阻塞在JAVA線程狀態(tài)里是 RUNNABLE 的狀態(tài)坝撑,不過(guò)他并不會(huì)占用用戶(hù)態(tài)的 CPU 時(shí)間片静秆,內(nèi)核在接受到數(shù)據(jù)后會(huì)結(jié)束這個(gè)阻塞.

小結(jié)

以上內(nèi)容希望可以給大家?guī)?lái)幫助,如果覺(jué)得有用的話(huà)巡李,可以點(diǎn)贊關(guān)注我抚笔,后續(xù)會(huì)持續(xù)更新我對(duì)JAVA相關(guān)知識(shí)的見(jiàn)解。如有不同的見(jiàn)解歡迎在評(píng)論區(qū)留言侨拦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末殊橙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子狱从,更是在濱河造成了極大的恐慌膨蛮,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件季研,死亡現(xiàn)場(chǎng)離奇詭異敞葛,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)训貌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)制肮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)冒窍,“玉大人,你說(shuō)我怎么就攤上這事豺鼻∽垡海” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵儒飒,是天一觀的道長(zhǎng)谬莹。 經(jīng)常有香客問(wèn)我,道長(zhǎng)桩了,這世上最難降的妖魔是什么附帽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮井誉,結(jié)果婚禮上蕉扮,老公的妹妹穿的比我還像新娘。我一直安慰自己颗圣,他們只是感情好喳钟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著在岂,像睡著了一般奔则。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蔽午,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天易茬,我揣著相機(jī)與錄音,去河邊找鬼及老。 笑死抽莱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的写半。 我是一名探鬼主播岸蜗,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼叠蝇!你這毒婦竟也來(lái)了璃岳?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤悔捶,失蹤者是張志新(化名)和其女友劉穎铃慷,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蜕该,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡犁柜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了堂淡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片馋缅。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扒腕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萤悴,到底是詐尸還是另有隱情瘾腰,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布覆履,位于F島的核電站蹋盆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏硝全。R本人自食惡果不足惜栖雾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伟众。 院中可真熱鬧析藕,春花似錦、人聲如沸凳厢。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)数初。三九已至,卻和暖如春梗顺,著一層夾襖步出監(jiān)牢的瞬間泡孩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工寺谤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仑鸥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓变屁,卻偏偏與公主長(zhǎng)得像眼俊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粟关,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容