再來(lái)捋捋Java 線程狀態(tài)

前言

線程并發(fā)系列文章:

Java 線程基礎(chǔ)
Java 線程狀態(tài)
Java “優(yōu)雅”地中斷線程-實(shí)踐篇
Java “優(yōu)雅”地中斷線程-原理篇
真正理解Java Volatile的妙用
Java ThreadLocal你之前了解的可能有誤
Java Unsafe/CAS/LockSupport 應(yīng)用與原理
Java 并發(fā)"鎖"的本質(zhì)(一步步實(shí)現(xiàn)鎖)
Java Synchronized實(shí)現(xiàn)互斥之應(yīng)用與源碼初探
Java 對(duì)象頭分析與使用(Synchronized相關(guān))
Java Synchronized 偏向鎖/輕量級(jí)鎖/重量級(jí)鎖的演變過(guò)程
Java Synchronized 重量級(jí)鎖原理深入剖析上(互斥篇)
Java Synchronized 重量級(jí)鎖原理深入剖析下(同步篇)
Java并發(fā)之 AQS 深入解析(上)
Java并發(fā)之 AQS 深入解析(下)
Java Thread.sleep/Thread.join/Thread.yield/Object.wait/Condition.await 詳解
Java 并發(fā)之 ReentrantLock 深入分析(與Synchronized區(qū)別)
Java 并發(fā)之 ReentrantReadWriteLock 深入分析
Java Semaphore/CountDownLatch/CyclicBarrier 深入解析(原理篇)
Java Semaphore/CountDownLatch/CyclicBarrier 深入解析(應(yīng)用篇)
最詳細(xì)的圖文解析Java各種鎖(終極篇)
線程池必懂系列

在現(xiàn)代操作系統(tǒng)里烫幕,有進(jìn)程和線程的概念,我們先來(lái)簡(jiǎn)單了解一下這兩個(gè)概念畏浆。

  • 進(jìn)程是資源分派的基本單位
  • 線程是cpu調(diào)度基本單位

剛開(kāi)始,計(jì)算機(jī)系統(tǒng)比較簡(jiǎn)單面睛,設(shè)計(jì)者把cpu上執(zhí)行的每個(gè)任務(wù)抽象為進(jìn)程纪隙,操作系統(tǒng)統(tǒng)一管理進(jìn)程的調(diào)度舔琅,給每個(gè)進(jìn)程分配地址空間、外設(shè)等吝秕,這些統(tǒng)稱(chēng)為進(jìn)程的資源泊脐。當(dāng)cpu執(zhí)行A進(jìn)程過(guò)程中,發(fā)現(xiàn)A的時(shí)間片已經(jīng)耗盡郭膛,因此先將A進(jìn)程掛起(釋放cpu)晨抡,并保存進(jìn)程A的上下文,然后再調(diào)度進(jìn)程B则剃。后來(lái)發(fā)現(xiàn)每次切換進(jìn)程的上下文都比較耗時(shí)耘柱,浪費(fèi)有限的系統(tǒng)資源,因此將進(jìn)程再劃分為粒度更小的單位棍现,稱(chēng)之為線程(實(shí)際上是cpu上的一個(gè)執(zhí)行流)调煎,進(jìn)程內(nèi)的線程共享進(jìn)程的資源,因此cpu切換線程所花代價(jià)更小己肮。

Linux進(jìn)程狀態(tài)

cpu同一時(shí)刻只能處理一個(gè)進(jìn)程士袄,有可能進(jìn)程還在排隊(duì)等待cpu執(zhí)行悲关,可能在某個(gè)地方等待資源(如輸入事件等),因此操作系統(tǒng)給予進(jìn)程狀態(tài)來(lái)標(biāo)識(shí)進(jìn)程某個(gè)時(shí)刻的狀態(tài)娄柳。[1]


image.png

如上圖寓辱,進(jìn)程狀態(tài)為ready(就緒),running(運(yùn)行中)赤拒,waiting(等待)
實(shí)際上線程狀態(tài)也可以類(lèi)比進(jìn)程狀態(tài)秫筏。

Java線程狀態(tài)

Java是門(mén)支持多線程的語(yǔ)言,Java線程是JVM實(shí)現(xiàn)的挎挖,JVM是OS的一個(gè)進(jìn)程这敬,Java線程對(duì)于OS來(lái)說(shuō)是透明的。接下來(lái)我們就Java線程類(lèi)Thread入手了解一下Java線程狀態(tài)蕉朵。

    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

注釋比較清晰崔涂,這6種狀態(tài)涵蓋了Thread整個(gè)生命周期,簡(jiǎn)單概括一下:

1始衅、NEW:構(gòu)造了thread實(shí)例冷蚂,但是還沒(méi)有start
2、RUNNABLE:線程正在運(yùn)行或者正等待被cpu執(zhí)行
3汛闸、BLOCKED:線程調(diào)用synchronized關(guān)鍵字等待獲取monitor鎖
4帝雇、WAITING:線程調(diào)用了無(wú)超時(shí)的wait、join蛉拙、park方法
5、TIMED_WAITING:線程調(diào)用了有超時(shí)的wait彻亲、sleep孕锄、join、parkNanos苞尝、parkUntil方法
6畸肆、TERMINATED:線程終止/完成了運(yùn)行

線程各種狀態(tài)之間是如何轉(zhuǎn)換的呢?用圖說(shuō)明:[2]


image.png

這張圖清晰展示了各個(gè)狀態(tài)之間的關(guān)系宙址,大家可能注意到圖上多了兩個(gè)狀態(tài):Ready和Running轴脐,而少了RUNNABLE狀態(tài)。還記得之前說(shuō)的RUNNABLE:“線程正在運(yùn)行或者正等待被cpu執(zhí)行”抡砂,實(shí)際上RUNNABLE可以認(rèn)為是Ready和Running狀態(tài)的結(jié)合體大咱,Ready表示就緒狀態(tài),Running表示正在運(yùn)行的狀態(tài)注益。

代碼驗(yàn)證狀態(tài)

上面的狀態(tài)描述可能比較抽象碴巾,Java 1.5之后有個(gè)方法:getState() 用來(lái)獲取線程的狀態(tài),因此我們可以打印該值來(lái)觀察狀態(tài)之間的流轉(zhuǎn)丑搔。

驗(yàn)證BLOCKED狀態(tài)

public class TestThreadState {

    public static void main(String args[]) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("t1 getted lock");
                    while(true);
                }
            }
        }, "t1");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("t2 getted lock");
                }
            }
        }, "t2");

        //test state
        t1.start();
        sleep(2);
        t2.start();

        while(true) {
            System.out.println("t1 state:" + t1.getState());
            System.out.println("t2 state:" + t2.getState());
            sleep(2);
        }

    }

    private static void sleep(long time) {
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (Exception e) {
        }
    }

}
image.png

t1先獲得monitor lock厦瓢,并一直循環(huán)不釋放鎖提揍,此時(shí)狀態(tài)為RUNNABLE;t2嘗試獲取monitor lock失敗煮仇,并阻塞劳跃,此時(shí)狀態(tài)為BLOCKED。
需要注意的是浙垫,如果使用ReentrantLock刨仑,線程因ReentrantLock阻塞,此時(shí)線程狀態(tài)為WAITING绞呈。

驗(yàn)證WAITING/TIMED_WAITING狀態(tài)

將上面的例子稍微修改為:

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("t1 getted lock");
                    try {
                        object.wait();
                        System.out.println("t1 get signal from t2");
                    } catch (Exception e) {

                    }
                }
            }
        }, "t1");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("t2 getted lock");
                    sleep(3);
                    System.out.println("t2 notify");
                    try {
                        object.notify();
                    } catch (Exception e) {

                    }
                    //先睡眠
                    sleep(3);
                }
            }
        }, "t2");
image.png

t1先獲得鎖贸人,并調(diào)用wait阻塞線程和放棄鎖,此時(shí)狀態(tài)為:WAITING佃声。t2獲取鎖成功艺智,并睡眠3s,此時(shí)狀態(tài)為:TIMED_WAITING圾亏。t2睡眠結(jié)束后調(diào)用notify喚醒t1十拣,此時(shí)t2繼續(xù)睡眠,狀態(tài)為:TIMED_WAITING志鹃。t1收到notify信息后夭问,嘗試獲取鎖,但由于此時(shí)t2還在睡眠沒(méi)有釋放鎖曹铃,因此t1被阻塞缰趋,狀態(tài)為BlOCKED。t2睡眠結(jié)束陕见,釋放鎖秘血,t1獲得鎖并接著打印“t1 get signal from t2”語(yǔ)句,最終t1评甜、t2終止了運(yùn)行灰粮,處在TERMINATED狀態(tài)。

Join和Yield

Join是Thread類(lèi)成員方法忍坷,該方法作用是當(dāng)前線程等待某個(gè)線程結(jié)束后再繼續(xù)執(zhí)行后續(xù)代碼粘舟。
Yield是Thread類(lèi)靜態(tài)方法,該方法作用是讓當(dāng)前線程放棄cpu佩研,并等待cpu重新調(diào)度(當(dāng)前線程可能再次獲得cpu)柑肴。通俗點(diǎn)的說(shuō)法是給其它線程一個(gè)機(jī)會(huì),讓大家回到同一起跑線韧骗,至于誰(shuí)被cpu選中嘉抒,看個(gè)人造化。

public class TestJoin {
    public static void main(String args[]) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    System.out.println("t1 end");
                } catch (Exception e) {

                }
            }
        }, "t1");
        t1.start();

        try {
            System.out.println("wait t1 terminate");
            t1.join();
        } catch (Exception e) {

        }

        System.out.println("main thread end");
    }
}

main thread等待t1執(zhí)行完成再繼續(xù)執(zhí)行袍暴。
網(wǎng)上不少文章列舉Yield用法示例:

public class TestYield {
    public static void main(String args[]) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    if (i == 3) {
                        Thread.yield();
                    }
                    System.out.println("t1->" + i);
                }
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("t2->" + i);
                }
            }
        }, "t2");
        t2.start();
    }
}

t1在執(zhí)行期間調(diào)用yield()方法些侍,放棄cpu隶症,這時(shí)候t2就可以拿到cpu。實(shí)際上打印結(jié)果的順序充滿不確定性岗宣,不能完全確定是由于yield()方法引起的差異蚂会,因此上面的例子不夠充分。而我們平時(shí)使用yield()方法的情況很少耗式,基本無(wú)需關(guān)注胁住。

LockSupport

用于線程阻塞與喚醒,更詳細(xì)的解釋請(qǐng)查看:Java Unsafe/CAS/LockSupport 應(yīng)用與原理

線程狀態(tài)的意義

在開(kāi)發(fā)的過(guò)程中刊咳,免不了用到多線程彪见。多線程之間的協(xié)作(同步、互斥)需要正確有序的進(jìn)行娱挨,在程序運(yùn)行過(guò)程中如果出現(xiàn)了多線程問(wèn)題一般都比較隨機(jī)不容易復(fù)現(xiàn)(死鎖等)余指,這個(gè)時(shí)候可以通過(guò)打印線程狀態(tài)(jstack)來(lái)分析各個(gè)線程狀態(tài),到底處在什么狀態(tài)跷坝?因?yàn)槭裁丛蛱幵谶@個(gè)狀態(tài)酵镜?實(shí)際應(yīng)該進(jìn)行到哪個(gè)狀態(tài)?這樣對(duì)于問(wèn)題的分析就會(huì)做到有的放矢柴钻,提高效率淮韭。
對(duì)如何中斷線程感興趣的同學(xué)請(qǐng)移步:Java “優(yōu)雅”地中斷線程

參考文章

[1] https://www.tecmint.com/linux-process-management/
[2] https://www.uml-diagrams.org/examples/java-6-thread-state-machine-diagram-example.html

您若喜歡,請(qǐng)點(diǎn)贊贴届、關(guān)注靠粪,您的鼓勵(lì)是我前進(jìn)的動(dòng)力

持續(xù)更新中,和我一起步步為營(yíng)系統(tǒng)毫蚓、深入學(xué)習(xí)Android

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庇配,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子绍些,更是在濱河造成了極大的恐慌,老刑警劉巖耀鸦,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柬批,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡袖订,警方通過(guò)查閱死者的電腦和手機(jī)氮帐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)洛姑,“玉大人上沐,你說(shuō)我怎么就攤上這事±惆” “怎么了参咙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵龄广,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蕴侧,道長(zhǎng)择同,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任净宵,我火速辦了婚禮敲才,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘择葡。我一直安慰自己紧武,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布敏储。 她就那樣靜靜地躺著阻星,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虹曙。 梳的紋絲不亂的頭發(fā)上迫横,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音酝碳,去河邊找鬼矾踱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛疏哗,可吹牛的內(nèi)容都是我干的呛讲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼返奉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贝搁!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起芽偏,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤雷逆,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后污尉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體膀哲,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年被碗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了某宪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锐朴,死狀恐怖兴喂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤衣迷,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布畏鼓,位于F島的核電站,受9級(jí)特大地震影響蘑险,放射性物質(zhì)發(fā)生泄漏滴肿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一佃迄、第九天 我趴在偏房一處隱蔽的房頂上張望泼差。 院中可真熱鬧,春花似錦呵俏、人聲如沸堆缘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吼肥。三九已至,卻和暖如春麻车,著一層夾襖步出監(jiān)牢的瞬間缀皱,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工动猬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啤斗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓赁咙,卻偏偏與公主長(zhǎng)得像钮莲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彼水,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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