36.線程狀態(tài)

一、線程狀態(tài)概述

線程的生命周期中,可能出現(xiàn)6個不同的狀態(tài),在java.lang.Thread.State中枚舉了這六種狀態(tài)

線程狀態(tài) 導(dǎo)致狀態(tài)發(fā)生的條件
New(新建) 線程剛被創(chuàng)建,但是并未啟動。還沒有調(diào)用start方法。
Runnable(可運行) 線程可以在java虛擬機中運行的狀態(tài),也可能沒有,這取決于操作系統(tǒng)處理器的調(diào)度。
Blocked(鎖阻塞) 當(dāng)一個線程試圖獲取一個對象鎖,二該對象鎖被其他的線程持有,則該線程進入Blocked狀態(tài);當(dāng)該線程持有鎖時,該線程將變成Runnable狀態(tài)
Waiting(無限等待) 一個線程在等待另一個線程執(zhí)行一個(喚醒)動作時,該線程進入Waiting狀態(tài)。進入這個狀態(tài)后是不能自動喚醒的,必須等待拎一個線程調(diào)用notify或者notifyAll方法才能喚醒。
Timed Waiting(計時等待) 同waiting狀態(tài),有幾個方法有超時參數(shù),調(diào)用他們將進入Timed Waiting狀態(tài),這一狀態(tài)將一保持到超時期滿或者接受到喚醒通知。帶有超時參數(shù)的常用方法有Thread.sleep、Object.wait
Terminated(被終止) 因為run方法正常退出而死亡,或者因為沒有捕獲的異常終止了run方法而死亡。或者強行終止

二、Timed Waiting(計時等待)

有兩個方法可以達到這個效果

  • Thread.sleep(long time)
  • Object.wait() : 創(chuàng)建一個靜態(tài)的Object對象,然后使用這個對象調(diào)用這個方法。

注意

  1. 進入TIMED_WAITING常見方式是調(diào)用sleep。(不一定要有協(xié)作關(guān)系)
  2. 為了讓其他線程有機會執(zhí)行,將Thread.sleep的調(diào)用放在線程run()之內(nèi)
  3. sleep與鎖無關(guān),線程睡眠到期自動蘇醒,返回到Runnable狀態(tài)。
  4. wait與鎖有關(guān),靠的就是鎖實現(xiàn)的。
TimedWaiting.png

sleep()中指定的時間是線程不會運行的最短時間。它不能保證結(jié)束睡眠后就立刻執(zhí)行該線程(操作系統(tǒng)調(diào)度)

三、Blocked(鎖阻塞)

一個正在阻塞等待一個監(jiān)視器鎖(鎖對象)的線程處于這一狀態(tài)。Waiting以及Time Waiting狀態(tài)在某種情況下也會進入阻塞狀態(tài)

Blocked.png

四、Waiting(無線等待)

一個正在無限期等待另一個線程執(zhí)行一個特別的(喚醒)動作的線程處于這一狀態(tài)。

public class Main {

    // 新建一個obj當(dāng)做鎖來耍耍。
    public static Object obj = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                synchronized(obj){
                    System.out.println("@@@等@@@- 我有鎖,我要調(diào)wait,進入waiting狀態(tài),同時釋放鎖對象。。。。");

                    // obj.wait(500); 這種就是計時等待。
                    try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); }

                    System.out.println("@@@等@@@- 有人把我喚醒了,之前可能沒有拿到鎖可能處于Blocked狀態(tài),但現(xiàn)在我拿到了鎖,我又可以為所欲為啦~~~~");
                }
            }
        }, "<- - - 等待線程 - - ->").start();

        new Thread(() -> {
            while (true) {
                // System.out.println("---喚--- ");
                try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
                // 申請鎖
                synchronized(obj){
                    System.out.println("---喚--- 我拿到鎖了,我先睡會兒~~~~");
                    try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
                    System.out.println("---喚--- OK,我釋放鎖了,等3秒鐘再去要鎖~~~\n");
                    obj.notify(); // 喚醒并釋放鎖
                }
            }
        },"<- - - 喚醒線程 - - ->").start();
    }
}

注意:一個調(diào)用了某個對象的 Object.wait 方法的線程會等待另一個線程調(diào)用此對象的Object.notify()方法 或 Object.notifyAll()方法

當(dāng)多個線程協(xié)作時,比如A,B線程,如果A線程在Runnable(可運行)狀態(tài)中調(diào)用了wait()方法那么A線程就進入了Waiting(無限等待)狀態(tài),同時失去了同步鎖。假如這個時候B線程獲取到了同步鎖,在運行狀態(tài)中調(diào)用了notify()方法,那么就會將無限等待的A線程喚醒。注意是喚醒,如果獲取到鎖對象,那么A線程喚醒后就進入Runnable(可運行)狀態(tài);如果沒有獲取鎖對象,那么就進入到Blocked(鎖阻塞狀態(tài))。

Waiting 線程狀態(tài)圖

WaitingBlockedRunnable.png

五、總結(jié):線程生命周期詳解圖。

線程生命周期詳解圖.png

Timed Waiting(計時等待) 與 Waiting(無限等待) 狀態(tài)聯(lián)系還是很緊密的,比如Waiting(無限等待) 狀態(tài)中wait方法是空參的,而timed waiting(計時等待) 中wait方法是帶參的。這種帶參的方法,其實是一種倒計時操作,相當(dāng)于我們生活中的小鬧鐘,我們設(shè)定好時間,到時通知,可是如果提前得到(喚醒)通知,那么設(shè)定好時間在通知也就顯得多此一舉了,那么這種設(shè)計方案其實是一舉兩得。如果沒有得到(喚醒)通知,那么線程就處于Timed Waiting狀態(tài),直到倒計時完畢自動醒來;如果在倒計時期間得到(喚醒)通知,那么線程從Timed Waiting狀態(tài)立刻喚醒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容