Java基礎(chǔ)-線程-線程的狀態(tài)

Java工程師知識(shí)樹 / Java基礎(chǔ)


線程狀態(tài)

每一個(gè)線程都有自己的局部變量表喧笔、程序計(jì)數(shù)器脸狸,以及生命周期等冶共,線程的生命周期中各個(gè)階段的狀態(tài)包括:

NEW(初始化),
RUNNABLE(可運(yùn)行的)裤园,
BLOCKED(鎖定或阻塞)询筏,
WAITING(等待)破花,
TIMED_WAITING(定時(shí)等待)滋尉,
TERMINATED(結(jié)束)玉控;
在Thread內(nèi)部類java.lang.Thread.State中定義了線程的生命周期中的所有狀態(tài)
public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

獲取線程的狀態(tài)方法:java.lang.Thread#getState

public State getState() {
    // get current thread state 獲取當(dāng)前線程的線程狀態(tài)
    return sun.misc.VM.toThreadState(threadStatus);
}

各個(gè)狀態(tài)詳情介紹:

1. 新建狀態(tài)(new):

? NEW狀態(tài)為使用new創(chuàng)建一個(gè)線程對(duì)象,在堆中分配內(nèi)存空間在調(diào)用start方法之前的線程所處的狀態(tài)狮惜;在此狀態(tài)下奸远,線程還沒啟動(dòng)既棺,只是創(chuàng)建了一個(gè)線程對(duì)象存儲(chǔ)在堆中讽挟;比如:

Thread t = new Thread(); // 此時(shí)t就屬于新建狀態(tài)

? 當(dāng)新建狀態(tài)下的線程對(duì)象調(diào)用了start方法懒叛,該線程對(duì)象就從新建狀態(tài)進(jìn)入可運(yùn)行狀態(tài)(runnable)

? 線程對(duì)象的start方法只能調(diào)用一次耽梅,多次調(diào)用會(huì)發(fā)生IllegalThreadStateException薛窥;

2. 可運(yùn)行狀態(tài)(runnable):

? 又可以細(xì)分成兩種狀態(tài),readyrunning眼姐,分別表示就緒狀態(tài)運(yùn)行狀態(tài)诅迷。將可運(yùn)行狀態(tài)細(xì)分為兩種狀態(tài)是因?yàn)椴淮_定線程是否獲取到CPU時(shí)間片。

? 就緒狀態(tài):線程對(duì)象調(diào)用start方法之后众旗,等待JVM的調(diào)度(此時(shí)該線程并沒有運(yùn)行)罢杉,還未開始運(yùn)行;

? 運(yùn)行狀態(tài):線程對(duì)象已獲得JVM調(diào)度贡歧,處在運(yùn)行中滩租;如果存在多個(gè)CPU,那么允許多個(gè)線程并行運(yùn)行利朵;

3. 阻塞狀態(tài)(blocked):

? 處于運(yùn)行中的線程因?yàn)槟承┰?strong>放棄CPU時(shí)間片律想,暫時(shí)停止運(yùn)行,就會(huì)進(jìn)入阻塞狀態(tài)绍弟;此時(shí)JVM不會(huì)給線程分配CPU時(shí)間片技即,直到線程重新進(jìn)入就緒狀態(tài)(ready)才有可能轉(zhuǎn)到運(yùn)行狀態(tài)樟遣;

? 阻塞狀態(tài)只能先進(jìn)入就緒狀態(tài)而叼,進(jìn)而由操作系統(tǒng)轉(zhuǎn)到運(yùn)行狀態(tài),不能直接進(jìn)入運(yùn)行狀態(tài)豹悬;

? 阻塞狀態(tài)產(chǎn)生的情況:

  • 當(dāng)A線程處于運(yùn)行中葵陵,試圖獲取同步鎖時(shí),但同步鎖卻被B線程獲取屿衅,此時(shí)JVM會(huì)把A線程存到共享資源對(duì)象的鎖池中埃难,A線程進(jìn)入阻塞狀態(tài);
  • 當(dāng)線程處于運(yùn)行狀態(tài)涤久,發(fā)出了IO請求時(shí)涡尘,該線程會(huì)進(jìn)入阻塞狀態(tài);
4. 等待狀態(tài)(waiting):

? 運(yùn)行中的線程調(diào)用了wait方法(無參數(shù)的wait方法)响迂,然后JVM會(huì)把該線程儲(chǔ)存到共享資源的對(duì)象等待池中考抄,該線程進(jìn)入等待狀態(tài);處于該狀態(tài)中的線程只能被其他線程喚醒(notify)蔗彤;

5. 計(jì)時(shí)等待狀態(tài)(timed waiting):

? 運(yùn)行中的線程調(diào)用了帶參數(shù)的wait方法或者sleep方法川梅,線程進(jìn)入計(jì)時(shí)等待狀態(tài)疯兼。

? 計(jì)時(shí)等待狀態(tài)下的線程不會(huì)釋放同步鎖/同步監(jiān)聽器;

? 計(jì)時(shí)等待狀態(tài)產(chǎn)生的情況:

  • 當(dāng)處于運(yùn)行中的線程贫途,調(diào)用了wait(long time)方法吧彪,JVM會(huì)把當(dāng)前線程存在共享資源對(duì)象等待池中,線程進(jìn)入計(jì)時(shí)等待狀態(tài)丢早;
  • 當(dāng)前線程執(zhí)行了sleep(long time)方法姨裸,該線程進(jìn)入計(jì)時(shí)等待狀態(tài);
6. 終止?fàn)顟B(tài)(terminated):

? 也可以稱為死亡狀態(tài)怨酝,表示線程終止傀缩,它的生命走到了盡頭;線程一旦終止,就不能再重啟啟動(dòng)农猬,否則會(huì)發(fā)生IllegalThreadStateException赡艰;

? 終止?fàn)顟B(tài)產(chǎn)生的情況:

  • 正常執(zhí)行完run方法而退出,壽終正寢斤葱,屬于正常死亡慷垮;
  • 線程執(zhí)行遇到異常而退出,線程中斷苦掘,屬于意外死亡换帜;

線程的生命周期狀態(tài)圖:

線程狀態(tài)

上圖解讀:

  1. 一個(gè)線程在其被創(chuàng)建之后它將處于 NEW(新建)狀態(tài),調(diào)用runnable.start()方法后開始運(yùn)行鹤啡,這時(shí)線程將轉(zhuǎn)換為RUNNABLE狀態(tài)惯驼。RUNNABLE共有2種狀態(tài),就緒(READY)和運(yùn)行中(RUNNING)递瑰,當(dāng)線程獲得了 cpu 時(shí)間片(timeslice)后就處于 RUNNING(運(yùn)行) 狀態(tài)祟牲,未獲得就處于READY(就緒)狀態(tài)
  2. RUNNABLE狀態(tài)抖部,我們可以轉(zhuǎn)換為waiting,time_waitingblocked三種狀態(tài)说贝。
  3. 進(jìn)入waiting狀態(tài)可以執(zhí)行wait()等方法,而waiting狀態(tài)轉(zhuǎn)換為running狀態(tài)可以通過notify等方法慎颗。
  4. 進(jìn)入time_waiting狀態(tài)可以通過sleep(long millis)方法或 wait(long millis)乡恕,返回runnbale狀態(tài)則可以通過notify等方法。
  5. 當(dāng)線程調(diào)用同步方法時(shí)俯萎,在沒有獲取到鎖的情況下傲宜,線程將會(huì)進(jìn)入到 BLOCKED(阻塞) 狀態(tài)
  6. 線程在執(zhí)行完Runnable 的run()方法之后將會(huì)進(jìn)入到 TERMINATED(終止) 狀態(tài)夫啊。

測試線程狀態(tài)代碼

package com.thread.study;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;

import java.util.Date;

public class TestThreadState {

    public static void main(String[] args) throws InterruptedException {
        final long ONE_SECONDS = 1000; // 1秒
        Thread mark = new Thread(() -> { // --> NEW
            synchronized (args) {
                try {
                    blocking(ONE_SECONDS); // --> BLOCKED
                    args.wait(); // --> WAITING
                    args.wait(ONE_SECONDS); // --> TIMED_WAITING
                } catch (InterruptedException e) {
                }
            }
        },"測試狀態(tài)的線程");
        printThreadState(mark);
        new Thread(() -> { // 執(zhí)行使 mark 線程啟動(dòng)后進(jìn)入監(jiān)視器鎖定阻塞的線程(等待獲取鎖)
            try {
                blocking(ONE_SECONDS + 500);
            } catch (InterruptedException e) {
            }
        }).start();
        Thread.sleep(ONE_SECONDS / 10);
        mark.start(); // 啟動(dòng) mark 線程 --> RUNNABLE
        printThreadState(mark); // 打印 mark 線程 start() 后的狀態(tài)
        Thread.sleep(ONE_SECONDS / 10);
        printThreadState(mark); // 打印 mark 線程 等待獲取 sync 的狀態(tài)
        Thread.sleep(ONE_SECONDS * 2 + 500); // 等待 mark 線程 blocking() 執(zhí)行完成后執(zhí)行 wait()
        printThreadState(mark); // 打印 mark 線程 wait() 時(shí)的狀態(tài)
        synchronized (args) {
            args.notify(); // 喚醒 mark 線程
        }
        Thread.sleep(ONE_SECONDS / 2); // 等待 mark 線程執(zhí)行 wait(long timeout)
        printThreadState(mark); // 獲取 mark 線程 wait(long timeout) 時(shí)的狀態(tài)
        Thread.sleep(ONE_SECONDS); // 等待 mark 線程執(zhí)行完畢
        printThreadState(mark); // 打印已執(zhí)行完成的狀態(tài) --> TERMINATED
    }

    static synchronized void blocking(long timeout) throws InterruptedException {
        Thread.sleep(timeout);
    }

    static void printThreadState(Thread stateThread) {
        System.out.println(stateThread.getName() + DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN) + "的線程狀態(tài)為" + stateThread.getState());
    }
}

打印結(jié)果:

測試狀態(tài)的線程2021-01-29 21:35:07.732的線程狀態(tài)為NEW
測試狀態(tài)的線程2021-01-29 21:35:07.935的線程狀態(tài)為RUNNABLE
測試狀態(tài)的線程2021-01-29 21:35:08.036的線程狀態(tài)為BLOCKED
測試狀態(tài)的線程2021-01-29 21:35:10.536的線程狀態(tài)為WAITING
測試狀態(tài)的線程2021-01-29 21:35:11.037的線程狀態(tài)為TIMED_WAITING
測試狀態(tài)的線程2021-01-29 21:35:12.039的線程狀態(tài)為TERMINATED
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末函卒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子撇眯,更是在濱河造成了極大的恐慌报嵌,老刑警劉巖虱咧,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锚国,居然都是意外死亡腕巡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門跷叉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逸雹,“玉大人,你說我怎么就攤上這事云挟。” “怎么了转质?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵园欣,是天一觀的道長。 經(jīng)常有香客問我休蟹,道長沸枯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任赂弓,我火速辦了婚禮绑榴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盈魁。我一直安慰自己翔怎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布杨耙。 她就那樣靜靜地躺著赤套,像睡著了一般。 火紅的嫁衣襯著肌膚如雪珊膜。 梳的紋絲不亂的頭發(fā)上容握,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音车柠,去河邊找鬼剔氏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛竹祷,可吹牛的內(nèi)容都是我干的谈跛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼溶褪,長吁一口氣:“原來是場噩夢啊……” “哼币旧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起猿妈,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤吹菱,失蹤者是張志新(化名)和其女友劉穎巍虫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鳍刷,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡占遥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了输瓜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓦胎。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖尤揣,靈堂內(nèi)的尸體忽然破棺而出搔啊,到底是詐尸還是另有隱情,我是刑警寧澤北戏,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布负芋,位于F島的核電站,受9級(jí)特大地震影響嗜愈,放射性物質(zhì)發(fā)生泄漏旧蛾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一蠕嫁、第九天 我趴在偏房一處隱蔽的房頂上張望锨天。 院中可真熱鬧,春花似錦剃毒、人聲如沸病袄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陪拘。三九已至,卻和暖如春纤壁,著一層夾襖步出監(jiān)牢的瞬間左刽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工酌媒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留欠痴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓秒咨,卻偏偏與公主長得像喇辽,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雨席,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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