java并發(fā)編程(三)java線程狀態(tài)與方法

一澳叉、線程的狀態(tài)

1.1 操作系統(tǒng)層面

在操作系統(tǒng)層面有五種狀態(tài):

操作系統(tǒng)層面的線程狀態(tài).png
  • 【初始狀態(tài)】?jī)H是在語(yǔ)言層面創(chuàng)建了線程對(duì)象倔既,還未與操作系統(tǒng)線程關(guān)聯(lián)
  • 【可運(yùn)行狀態(tài)】(就緒狀態(tài))指該線程已經(jīng)被創(chuàng)建(與操作系統(tǒng)線程關(guān)聯(lián))斋日,可以由 CPU 調(diào)度執(zhí)行
  • 【運(yùn)行狀態(tài)】指獲取了 CPU 時(shí)間片運(yùn)行中的狀態(tài)建瘫。當(dāng) CPU 時(shí)間片用完贯被,會(huì)從【運(yùn)行狀態(tài)】轉(zhuǎn)換至【可運(yùn)行狀態(tài)】噪窘,會(huì)導(dǎo)致線程的上下文切換
  • 【阻塞狀態(tài)】
    • 如果調(diào)用了阻塞 API,如 BIO 讀寫文件泞遗,這時(shí)該線程實(shí)際不會(huì)用到 CPU惰许,會(huì)導(dǎo)致線程上下文切換,進(jìn)入【阻塞狀態(tài)】
    • 等 BIO 操作完畢史辙,會(huì)由操作系統(tǒng)喚醒阻塞的線程汹买,轉(zhuǎn)換至【可運(yùn)行狀態(tài)】
    • 與【可運(yùn)行狀態(tài)】的區(qū)別是,對(duì)【阻塞狀態(tài)】的線程來(lái)說只要它們一直不喚醒聊倔,調(diào)度器就一直不會(huì)考慮調(diào)度它們
  • 【終止?fàn)顟B(tài)】表示線程已經(jīng)執(zhí)行完畢晦毙,生命周期已經(jīng)結(jié)束,不會(huì)再轉(zhuǎn)換為其它狀態(tài)

1.2 Java的Thread狀態(tài)

Thread的狀態(tài)耙蔑,是一個(gè)enum见妒,有六種狀態(tài),如下所示:

public enum State {
    /**
     * 初始
     */
    NEW,
    /**
     * 可運(yùn)行
     */
    RUNNABLE,
    /**
     * 阻塞
     */
    BLOCKED,
    /**
     * 等待
     */
    WAITING,
    /**
     * 超時(shí)等待
     */
    TIMED_WAITING,
    /**
     * 終止
     */
    TERMINATED;
}
JAVA Thread狀態(tài).png
  • NEW 線程剛被創(chuàng)建甸陌,但是還沒有調(diào)用 start() 方法
  • RUNNABLE 當(dāng)調(diào)用了 start() 方法之后须揣,注意盐股,Java API 層面的 RUNNABLE 狀態(tài)涵蓋了 操作系統(tǒng) 層面的【可運(yùn)行狀態(tài)】、【運(yùn)行狀態(tài)】和【阻塞狀態(tài)】(由于 BIO 導(dǎo)致的線程阻塞耻卡,在 Java 里無(wú)法區(qū)分疯汁,仍然認(rèn)為是可運(yùn)行)
  • BLOCKED , WAITING 卵酪, TIMED_WAITING 都是 Java API 層面對(duì)【阻塞狀態(tài)】的細(xì)分涛目,在后面第三節(jié)(方法與狀態(tài)轉(zhuǎn)換)會(huì)講解
  • TERMINATED 當(dāng)線程代碼運(yùn)行結(jié)束

二、Thread的常用方法

2.1 常用方法

方法名 static 功能說明 注意
start() 啟動(dòng)一個(gè)線程凛澎,線程當(dāng)中運(yùn)行run()方法中的代碼 start 方法只是讓線程進(jìn)入就緒霹肝,里面代碼不一定立刻運(yùn)行(CPU 的時(shí)間片還沒分給它)。每個(gè)線程對(duì)象的start方法只能調(diào)用一次塑煎,如果調(diào)用了多次會(huì)出現(xiàn)IllegalThreadStateException
run() 線程啟動(dòng)后調(diào)用的方法 如果在構(gòu)造 Thread 對(duì)象時(shí)傳遞了 Runnable 參數(shù)沫换,則線程啟動(dòng)后會(huì)調(diào)用 Runnable 中的 run 方法,否則默認(rèn)不執(zhí)行任何操作最铁。但可以創(chuàng)建 Thread 的子類對(duì)象讯赏,來(lái)覆蓋默認(rèn)行為;

class ExtendThread extends Thread {
? ?@Override
?? public void run() {
????System.out.println("繼承Thread類方式");
???}
}
join() 等待當(dāng)前線程執(zhí)行結(jié)束 在當(dāng)前執(zhí)行線程a中,另一個(gè)線程b調(diào)用該方法冷尉,則線程a進(jìn)入WAITING狀態(tài)漱挎,直到線程b執(zhí)行完畢,線程a繼續(xù)執(zhí)行

原理:調(diào)用者輪詢檢查線程 alive 狀態(tài)

等價(jià)于下面的代碼:
synchronized (t1) {
???// 調(diào)用者線程進(jìn)入 t1 的 waitSet 等待, 直到 t1 運(yùn)行結(jié)束
??????while (t1.isAlive()) {
??????t1.wait(0);
???}
}
join(long n) 等待當(dāng)前線程運(yùn)行結(jié)束,最多等待 n毫秒
getId() 獲取線程長(zhǎng)整型的 id 唯一id
getName() 獲取線程名稱
setName(String) 修改線程名稱
getPriority() 獲取線程優(yōu)先級(jí)
setPriority(int) 修改線程優(yōu)先級(jí) java中規(guī)定線程優(yōu)先級(jí)是1~10 的整數(shù)雀哨,較大的優(yōu)先級(jí)能提高該線程被 CPU 調(diào)度的機(jī)率
getState() 獲取線程狀態(tài) Java 中線程狀態(tài)是用 6 個(gè) enum 表示磕谅,分別為:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
isInterrupted() 判斷是否被打斷 不會(huì)清除 打斷標(biāo)記
isAlive() 判斷線程是否存活(是否運(yùn)行完畢)
interrupt() 打斷線程 如果被打斷線程正在 sleep,wait雾棺,join 會(huì)導(dǎo)致被打斷的線程拋出InterruptedException膊夹,并清除 打斷標(biāo)記 ;

如果打斷的正在運(yùn)行的線程捌浩,則會(huì)設(shè)置 打斷標(biāo)記 放刨;

park 的線程被打斷,也會(huì)設(shè)置 打斷標(biāo)記尸饺。
interrupted() static 判斷當(dāng)前線程是否被打斷 會(huì)清除 打斷標(biāo)記
currentThread() static 獲取當(dāng)前正在執(zhí)行的線程
sleep(long n) static 讓當(dāng)前執(zhí)行的線程休眠n毫秒进统,休眠時(shí)讓出 cpu的時(shí)間片給其它線程
yied() static 提示線程調(diào)度器讓出當(dāng)前線程對(duì)CPU的使用 主要是為了測(cè)試和調(diào)試

2.2 sleep和yied

2.2.1 sleep

  1. 調(diào)用 sleep 會(huì)讓當(dāng)前線程從 Running 進(jìn)入 Timed Waiting 狀態(tài)(阻塞)
  2. 其它線程可以使用 interrupt 方法打斷正在睡眠的線程,這時(shí) sleep 方法會(huì)拋出 InterruptedException
  3. 睡眠結(jié)束后的線程未必會(huì)立刻得到執(zhí)行
  4. 建議用 TimeUnit 的 sleep 代替 Thread 的 sleep 來(lái)獲得更好的可讀性(內(nèi)部也是Thread.sleep)
TimeUnit.SECONDS.sleep(5);

2.2.2 yied

  1. 調(diào)用 yield 會(huì)讓當(dāng)前線程從 Running 進(jìn)入 Runnable 就緒狀態(tài)浪听,然后調(diào)度執(zhí)行其它線程
  2. 具體的實(shí)現(xiàn)依賴于操作系統(tǒng)的任務(wù)調(diào)度器

2.3 interrupt 方法詳解

線程的Thread.interrupt()方法是中斷線程螟碎,將會(huì)設(shè)置該線程的中斷狀態(tài),即設(shè)置為true馋辈。

其作用僅僅而已抚芦,線程關(guān)閉還是繼續(xù)執(zhí)行業(yè)務(wù)進(jìn)程應(yīng)該由我們的程序自己進(jìn)行判斷。

針對(duì)不同狀態(tài)下的線程進(jìn)行中斷操作迈螟,會(huì)有不一樣的結(jié)果:

2.3.1 中斷wait() 叉抡、join()、sleep()

如果此線程在調(diào)用Object類的wait() 答毫、 wait(long)或wait(long, int)方法或join() 褥民、 join(long) 、 join(long, int)被阻塞洗搂、 sleep(long)或sleep(long, int) 消返,此類的方法,則其中斷狀態(tài)將被清除并收到InterruptedException 耘拇。

以sleep舉例:

    public static void main(String[] args) {

        Thread t = new Thread(() -> {
            System.out.println("打斷狀態(tài):" + Thread.currentThread().isInterrupted());
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("打斷后的狀態(tài):" + Thread.currentThread().isInterrupted());
        });
        t.start();
        t.interrupt();
        System.out.println("打斷狀態(tài):" + t.isInterrupted());
    }

結(jié)果:

打斷狀態(tài):true
打斷狀態(tài):true
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    at com.cloud.bssp.thread.InterruptTest.lambda$main$0(InterruptTest.java:18)
    at java.lang.Thread.run(Thread.java:748)

2.3.2 中斷正常線程

正常線程將會(huì)被設(shè)置中斷標(biāo)記位撵颊,我們可以根據(jù)該標(biāo)記位判斷線程如何執(zhí)行,如下所示:

    /**
     * 中斷正常線程
     *
     * @param args
     */
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true){
                if(Thread.currentThread().isInterrupted()){
                    System.out.println("中斷狀態(tài):" + Thread.currentThread().isInterrupted());
                    break;
                }
            }
        });
        t.start();
        t.interrupt();
    }

結(jié)果:

中斷狀態(tài):true

2.3.3 中斷park線程

不會(huì)使中斷狀態(tài)清除惫叛。

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("park");
                LockSupport.park();
                System.out.println("unpark");
                System.out.println("中斷狀態(tài):" + Thread.currentThread().isInterrupted());
                if (Thread.currentThread().isInterrupted()) {
                    break;
                }
            }
        });
        t.start();
        TimeUnit.SECONDS.sleep(1);
        t.interrupt();
    }

結(jié)果:

park
unpark
中斷狀態(tài):true

如果在park之前倡勇,線程已經(jīng)是中斷狀態(tài)了,則會(huì)使park失效嘉涌,如下所示妻熊,除了首次park成功能成功,被中斷后仑最,后面的park都失效了:

   /**
     * 中斷park
     *
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("打斷狀態(tài):" + Thread.currentThread().isInterrupted());
                System.out.println("park..." + i);
                LockSupport.park();
            }
        });
        t1.start();
        TimeUnit.SECONDS.sleep(1);
        t1.interrupt();
    }

結(jié)果:

打斷狀態(tài):false
park...0
打斷狀態(tài):true
park...1
打斷狀態(tài):true
park...2
打斷狀態(tài):true
park...3
打斷狀態(tài):true
park...4

可以 Thread.interrupted() 方法去除中斷標(biāo)記:

2.3.5 不推薦使用的方法

方法名稱 描述
stop() 停止線程運(yùn)行。不安全的,并將會(huì)在未來(lái)版本刪除
suspend() 掛起(暫停)線程運(yùn)行逼龟,此方法已被棄用典徘,因?yàn)樗举|(zhì)上容易死鎖
resume() 恢復(fù)線程運(yùn)行。此方法僅用于suspend 预皇,已被棄用损敷,因?yàn)樗菀姿梨i

2.3.4 其他中斷

  • 如果此線程在InterruptibleChannel的 I/O 操作中被阻塞,則通道將關(guān)閉深啤,線程的中斷狀態(tài)將被設(shè)置拗馒,并且線程將收到j(luò)ava.nio.channels.ClosedByInterruptException 。

  • 如果該線程在java.nio.channels.Selector被阻塞溯街,則該線程的中斷狀態(tài)將被設(shè)置诱桂,并且它將立即從選擇操作中返回,可能具有非零值呈昔,就像調(diào)用了選擇器的wakeup方法挥等。

三、方法與狀態(tài)轉(zhuǎn)換

如下圖所示堤尾,線的右側(cè)表示執(zhí)行的方法:

image.png

下面具體分析方法和狀態(tài)轉(zhuǎn)換肝劲,假設(shè)有一個(gè)線程Thread t:

1.NEW --> RUNNABLE

執(zhí)行t.start()

2.RUNNABLE <--> WAITING

此種狀態(tài)轉(zhuǎn)換分三種情況:
1)t 線程用 synchronized(obj) 獲取了對(duì)象鎖后,調(diào)用 obj.wait() 方法時(shí),t 線程從 RUNNABLE --> WAITING

調(diào)用 obj.notify() 辞槐, obj.notifyAll() 掷漱, t.interrupt() 時(shí):

  • 競(jìng)爭(zhēng)鎖成功,t 線程從 WAITING --> RUNNABLE
  • 競(jìng)爭(zhēng)鎖失敗榄檬,t 線程從 WAITING --> BLOCKED

2)當(dāng)前線程調(diào)用 t.join() 方法時(shí)卜范,當(dāng)前線程從 RUNNABLE --> WAITING

注意是當(dāng)前線程在t 線程對(duì)象的監(jiān)視器上等待

當(dāng)前線程會(huì)等到t執(zhí)行結(jié)束后或調(diào)用了當(dāng)前線程的 interrupt() 時(shí),WAITING --> RUNNABLE鹿榜。

3)當(dāng)前線程調(diào)用 LockSupport.park() 方法會(huì)讓當(dāng)前線程從 RUNNABLE --> WAITING

調(diào)用 LockSupport.unpark(目標(biāo)線程) 或調(diào)用了線程 的 interrupt() 海雪,會(huì)讓目標(biāo)線程從 WAITING --> RUNNABLE

3.RUNNABLE <--> TIMED_WAITING

此種狀態(tài)轉(zhuǎn)換分四種情況:

1) t 線程用 synchronized(obj) 獲取了對(duì)象鎖后,調(diào)用 obj.wait(long n) 方法時(shí)舱殿,t 線程從 RUNNABLE --> TIMED_WAITING

t 線程等待時(shí)間超過了 n 毫秒奥裸,或調(diào)用 obj.notify() , obj.notifyAll() 沪袭, t.interrupt() 時(shí):

  • 競(jìng)爭(zhēng)鎖成功湾宙,t 線程從 TIMED_WAITING --> RUNNABLE
  • 競(jìng)爭(zhēng)鎖失敗,t 線程從 TIMED_WAITING --> BLOCKED

2)當(dāng)前線程調(diào)用 t.join(long n) 方法時(shí)枝恋,當(dāng)前線程從 RUNNABLE --> TIMED_WAITING

注意是當(dāng)前線程在t 線程對(duì)象的監(jiān)視器上等待

當(dāng)前線程等待時(shí)間超過了 n 毫秒创倔,或t 線程運(yùn)行結(jié)束,或調(diào)用了當(dāng)前線程的 interrupt() 時(shí)焚碌,當(dāng)前線程從TIMED_WAITING --> RUNNABLE

3)當(dāng)前線程調(diào)用 Thread.sleep(long n) 畦攘,當(dāng)前線程從 RUNNABLE --> TIMED_WAITING

當(dāng)前線程等待時(shí)間超過了 n 毫秒,當(dāng)前線程從 TIMED_WAITING --> RUNNABLE

4)當(dāng)前線程調(diào)用 LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis) 時(shí)十电,當(dāng)前線程從 RUNNABLE --> TIMED_WAITING

調(diào)用 LockSupport.unpark(目標(biāo)線程) 或調(diào)用了線程 的 interrupt() 知押,或是等待超時(shí),會(huì)讓目標(biāo)線程從
TIMED_WAITING--> RUNNABLE

4.RUNNABLE <--> BLOCKED

t 線程用 synchronized(obj) 獲取了對(duì)象鎖時(shí)如果競(jìng)爭(zhēng)失敗鹃骂,從 RUNNABLE --> BLOCKED

持 obj 鎖線程的同步代碼塊執(zhí)行完畢台盯,會(huì)喚醒該對(duì)象上所有 BLOCKED 的線程重新競(jìng)爭(zhēng),如果其中 t 線程競(jìng)爭(zhēng)成功畏线,從 BLOCKED --> RUNNABLE 静盅,其它失敗的線程仍然 BLOCKED

5.RUNNABLE <--> TERMINATED

當(dāng)前線程所有代碼運(yùn)行完畢,進(jìn)入 TERMINATED

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寝殴,一起剝皮案震驚了整個(gè)濱河市蒿叠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蚣常,老刑警劉巖市咽,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異抵蚊,居然都是意外死亡施绎,警方通過查閱死者的電腦和手機(jī)溯革,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谷醉,“玉大人致稀,你說我怎么就攤上這事」陆簦” “怎么了豺裆?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵拒秘,是天一觀的道長(zhǎng)号显。 經(jīng)常有香客問我,道長(zhǎng)躺酒,這世上最難降的妖魔是什么押蚤? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮羹应,結(jié)果婚禮上揽碘,老公的妹妹穿的比我還像新娘。我一直安慰自己园匹,他們只是感情好雳刺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著裸违,像睡著了一般掖桦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上供汛,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天枪汪,我揣著相機(jī)與錄音,去河邊找鬼怔昨。 笑死雀久,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的趁舀。 我是一名探鬼主播赖捌,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼矮烹!你這毒婦竟也來(lái)了越庇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤擂送,失蹤者是張志新(化名)和其女友劉穎悦荒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘹吨,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搬味,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碰纬。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萍聊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出悦析,到底是詐尸還是另有隱情寿桨,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布强戴,位于F島的核電站亭螟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏骑歹。R本人自食惡果不足惜预烙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望道媚。 院中可真熱鬧扁掸,春花似錦、人聲如沸最域。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)镀脂。三九已至牺蹄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狗热,已是汗流浹背钞馁。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匿刮,地道東北人僧凰。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像熟丸,于是被迫代替她去往敵國(guó)和親训措。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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