0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

在Java中的線程狀態(tài)轉(zhuǎn)換

冬至子 ? 來(lái)源:瑞煕share ? 作者:brevity wit ? 2023-06-02 10:07 ? 次閱讀

Java 中線程的生命周期中一共有 6 種狀態(tài)。New(新創(chuàng)建);Runnable(可運(yùn)行);Blocked(被阻塞);Waiting(等待);Timed Waiting(計(jì)時(shí)等待);Terminated(被終止)。如果想要確定線程當(dāng)前的狀態(tài),可以通過(guò) getState() 方法,并且線程在任何時(shí)刻只可能處于 1 種狀態(tài)。

New 新創(chuàng)建

圖片

New 表示線程被創(chuàng)建但尚未啟動(dòng)的狀態(tài):當(dāng)我們用 new Thread() 新建一個(gè)線程時(shí),如果線程沒(méi)有開始運(yùn)行 start() 方法,所以也沒(méi)有開始執(zhí)行 run() 方法里面的代碼,那么此時(shí)它的狀態(tài)就是 New。而一旦線程調(diào)用了 start(),它的狀態(tài)就會(huì)從 New 變成 Runnable,也就是狀態(tài)轉(zhuǎn)換圖中中間的這個(gè)大方框里的內(nèi)容。

Runnable 可運(yùn)行

圖片

Java 中的 Runable 狀態(tài)對(duì)應(yīng)操作系統(tǒng)線程狀態(tài)中的兩種狀態(tài),分別是 Running 和 Ready,也就是說(shuō),Java 中處于 Runnable 狀態(tài)的線程有可能正在執(zhí)行,也有可能沒(méi)有正在執(zhí)行,正在等待被分配 CPU 資源。所以,如果一個(gè)正在運(yùn)行的線程是 Runnable 狀態(tài),當(dāng)它運(yùn)行到任務(wù)的一半時(shí),執(zhí)行該線程的 CPU 被調(diào)度去做其他事情,導(dǎo)致該線程暫時(shí)不運(yùn)行,它的狀態(tài)依然不變,還是 Runnable,因?yàn)樗锌赡茈S時(shí)被調(diào)度回來(lái)繼續(xù)執(zhí)行任務(wù)。

圖片

接下來(lái),我們來(lái)看下 Runnable 下面的三個(gè)方框,它們統(tǒng)稱為阻塞狀態(tài),在 Java 中阻塞狀態(tài)通常不僅僅是 Blocked,實(shí)際上它包括三種狀態(tài),分別是 Blocked(被阻塞)、Waiting(等待)、Timed Waiting(計(jì)時(shí)等待),這三 種狀態(tài)統(tǒng)稱為阻塞狀態(tài),下面我們來(lái)看看這三種狀態(tài)具體是什么含義。

Blocked 被阻塞

圖片

首先來(lái)看最簡(jiǎn)單的 Blocked,從箭頭的流轉(zhuǎn)方向可以看出,從 Runnable 狀態(tài)進(jìn)入 Blocked 狀態(tài)只有一種可能,就是進(jìn)入 synchronized 保護(hù)的代碼時(shí)沒(méi)有搶到 monitor 鎖,無(wú)論是進(jìn)入 synchronized 代碼塊,還是 synchronized 方法,都是一樣。我們?cè)偻铱?,?dāng)處于 Blocked 的線程搶到 monitor 鎖,就會(huì)從 Blocked 狀態(tài)回到Runnable 狀態(tài)。

Waiting 等待

圖片

我們?cè)倏纯?Waiting 狀態(tài),線程進(jìn)入 Waiting 狀態(tài)有三種可能性。

沒(méi)有設(shè)置 Timeout 參數(shù)的 Object.wait() 方法。

沒(méi)有設(shè)置 Timeout 參數(shù)的 Thread.join() 方法。

LockSupport.park() 方法。

Blocked 僅僅針對(duì) synchronized monitor 鎖,可是在 Java 中還有很多其他的鎖,比如 ReentrantLock,如果線程在獲取這種鎖時(shí)沒(méi)有搶到該鎖就會(huì)進(jìn)入 Waiting 狀態(tài),因?yàn)楸举|(zhì)上它執(zhí)行了 LockSupport.park() 方法,所以會(huì)進(jìn)入 Waiting 狀態(tài)。同樣,Object.wait() 和 Thread.join() 也會(huì)讓線程進(jìn)入 Waiting 狀態(tài)。Blocked 與 Waiting 的區(qū)別是 Blocked 在等待其他線程釋放 monitor 鎖,而 Waiting 則是在等待某個(gè)條件,比如 join 的線程執(zhí)行完畢,或者是 notify()/notifyAll() 。

Timed Waiting 限期等待

圖片

在 Waiting 上面是 Timed Waiting 狀態(tài),這兩個(gè)狀態(tài)是非常相似的,區(qū)別僅在于有沒(méi)有時(shí)間限制,Timed Waiting 會(huì)等待超時(shí),由系統(tǒng)自動(dòng)喚醒,或者在超時(shí)前被喚醒信號(hào)喚醒。

以下情況會(huì)讓線程進(jìn)入 Timed Waiting 狀態(tài)。

設(shè)置了時(shí)間參數(shù)的 Thread.sleep(long millis) 方法;

設(shè)置了時(shí)間參數(shù)的 Object.wait(long timeout) 方法;

設(shè)置了時(shí)間參數(shù)的 Thread.join(long millis) 方法;

設(shè)置了時(shí)間參數(shù)的 LockSupport.parkNanos(long nanos) 方法和 LockSupport.parkUntil(long deadline) 方法。

我們?cè)賮?lái)看下如何從這三種狀態(tài)流轉(zhuǎn)到下一個(gè)狀態(tài)。

圖片

想要從 Blocked 狀態(tài)進(jìn)入 Runnable 狀態(tài),要求線程獲取 monitor 鎖,而從 Waiting 狀態(tài)流轉(zhuǎn)到其他狀態(tài)則比較特殊,因?yàn)槭紫?Waiting 是不限時(shí)的,也就是說(shuō)無(wú)論過(guò)了多長(zhǎng)時(shí)間它都不會(huì)主動(dòng)恢復(fù)。

圖片

只有當(dāng)執(zhí)行了 LockSupport.unpark(),或者 join 的線程運(yùn)行結(jié)束,或者被中斷時(shí)才可以進(jìn)入 Runnable 狀態(tài)。

圖片

如果其他線程調(diào)用 notify() 或 notifyAll()來(lái)喚醒它,它會(huì)直接進(jìn)入 Blocked 狀態(tài),這是為什么呢?因?yàn)閱拘?Waiting 線程的線程如果調(diào)用 notify() 或 notifyAll(),要求必須首先持有該 monitor 鎖,所以處于 Waiting 狀態(tài)的線程被喚醒時(shí)拿不到該鎖,就會(huì)進(jìn)入 Blocked 狀態(tài),直到執(zhí)行了 notify()/notifyAll() 的喚醒它的線程執(zhí)行完畢并釋放 monitor 鎖,才可能輪到它去搶奪這把鎖,如果它能搶到,就會(huì)從 Blocked 狀態(tài)回到 Runnable 狀態(tài)。

圖片

同樣在 Timed Waiting 中執(zhí)行 notify() 和 notifyAll() 也是一樣的道理,它們會(huì)先進(jìn)入 Blocked 狀態(tài),然后搶奪鎖成功后,再回到 Runnable 狀態(tài)。

圖片

當(dāng)然對(duì)于 Timed Waiting 而言,如果它的超時(shí)時(shí)間到了且能直接獲取到鎖/join的線程運(yùn)行結(jié)束/被中斷/調(diào)用了LockSupport.unpark(),會(huì)直接恢復(fù)到 Runnable 狀態(tài),而無(wú)需經(jīng)歷 Blocked 狀態(tài)。

Terminated 終止

圖片

再來(lái)看看最后一種狀態(tài),Terminated 終止?fàn)顟B(tài),要想進(jìn)入這個(gè)狀態(tài)有兩種可能。run() 方法執(zhí)行完畢,線程正常退出。出現(xiàn)一個(gè)沒(méi)有捕獲的異常,終止了 run() 方法,最終導(dǎo)致意外終止。

注意點(diǎn)

最后我們?cè)倏淳€程轉(zhuǎn)換的兩個(gè)注意點(diǎn)。線程的狀態(tài)是需要按照箭頭方向來(lái)走的,比如線程從 New 狀態(tài)是不可以直接進(jìn)入 Blocked 狀態(tài)的,它需要先經(jīng)歷 Runnable 狀態(tài)。線程生命周期不可逆:一旦進(jìn)入 Runnable 狀態(tài)就不能回到 New 狀態(tài);一旦被終止就不可能再有任何狀態(tài)的變化。所以一個(gè)線程只能有一次 New 和 Terminated 狀態(tài),只有處于中間狀態(tài)才可以相互轉(zhuǎn)換。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2952

    瀏覽量

    104484
  • JAVA語(yǔ)言
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

    20062
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Java線程的用法

    本文將介紹一下Java線程的用法。 基礎(chǔ)介紹 什么是多線程 指的是一個(gè)進(jìn)程同時(shí)運(yùn)行多個(gè)線程
    的頭像 發(fā)表于 09-30 17:07 ?916次閱讀

    Java線程池包括哪些

    線程池是用來(lái)統(tǒng)一管理線程的, Java 創(chuàng)建和銷毀線程都是一件消耗資源的事情,
    的頭像 發(fā)表于 10-11 15:33 ?777次閱讀
    <b class='flag-5'>Java</b><b class='flag-5'>中</b>的<b class='flag-5'>線程</b>池包括哪些

    Java線程阻塞方法大全

    IO是操作系統(tǒng)實(shí)現(xiàn)的,Java代碼并沒(méi)有辦法直接接觸到操作系統(tǒng)。以下是詳細(xì)的喚醒方法:1. sleep() 方法sleep(毫秒),指定以毫秒為單位的時(shí)間,使線程該時(shí)間內(nèi)進(jìn)入線程阻塞
    發(fā)表于 04-02 15:42

    Java線程喚醒與阻塞規(guī)則

    IO是操作系統(tǒng)實(shí)現(xiàn)的,Java代碼并沒(méi)有辦法直接接觸到操作系統(tǒng)。以下是詳細(xì)的喚醒方法:1. sleep() 方法:sleep(毫秒),指定以毫秒為單位的時(shí)間,使線程該時(shí)間內(nèi)進(jìn)入線程
    發(fā)表于 07-06 15:11

    Java線程的五種狀態(tài)

    java線程的五種狀態(tài)其實(shí)要真正高清,只需要明白計(jì)算機(jī)操作系統(tǒng)中進(jìn)程的知識(shí),原理都是相同的。
    發(fā)表于 08-02 07:59

    Java守護(hù)線程和本地線程的區(qū)別

    java線程分為兩種:守護(hù)線程(Daemon)和用戶線程(User)。
    發(fā)表于 08-07 08:10

    java線程設(shè)計(jì)模式_結(jié)城浩

    JAVA線程設(shè)計(jì)模式》通過(guò)淺顯易懂的文字與實(shí)例來(lái)介紹JAVA線程相關(guān)的設(shè)計(jì)模式概念,并且通過(guò)實(shí)際的JAVA程序范例和UML圖示來(lái)一一解說(shuō)
    發(fā)表于 01-05 16:15 ?0次下載
    <b class='flag-5'>java</b>多<b class='flag-5'>線程</b>設(shè)計(jì)模式_結(jié)城浩

    java線程狀態(tài)圖和定義

    線程時(shí)由系統(tǒng)分配的,主要用來(lái)保存線程內(nèi)部所使用的數(shù)據(jù),如線程執(zhí)行函數(shù)中所定義的變量。 注意:Java的多
    發(fā)表于 09-27 10:44 ?0次下載
    <b class='flag-5'>java</b><b class='flag-5'>線程</b>的<b class='flag-5'>狀態(tài)</b>圖和定義

    Java線程總結(jié)之Queue

    Java線程應(yīng)用,隊(duì)列的使用率很高,多數(shù)生產(chǎn)消費(fèi)模型的首選數(shù)據(jù)結(jié)構(gòu)就是隊(duì)列。Java提供的線程
    發(fā)表于 11-28 16:14 ?3277次閱讀
    <b class='flag-5'>Java</b>多<b class='flag-5'>線程</b>總結(jié)之Queue

    java學(xué)習(xí)——java面試【事務(wù)、鎖、多線程】資料整理

    本文檔內(nèi)容介紹了基于java學(xué)習(xí)java面試【事務(wù)、鎖、多線程】資料整理,供參考
    發(fā)表于 03-13 13:53 ?0次下載

    Java教程之零點(diǎn)起飛學(xué)Java線程資料說(shuō)明

    線程編程是提高應(yīng)用程序性能的重要手段之一。Java平臺(tái)從開始就被設(shè)計(jì)成為多線程環(huán)境,從語(yǔ)言級(jí)上支持多線程。
    發(fā)表于 02-20 10:41 ?3次下載
    <b class='flag-5'>Java</b>教程之零點(diǎn)起飛學(xué)<b class='flag-5'>Java</b>的<b class='flag-5'>線程</b>資料說(shuō)明

    Java線程學(xué)習(xí)基礎(chǔ)詳解

    使用 new 關(guān)鍵字和 Thread 類或其子類建立一個(gè)線程對(duì)象后,該線程對(duì)象就處于新建狀態(tài)。它保持這個(gè)狀態(tài)直到程序 start() 這個(gè)線程
    的頭像 發(fā)表于 12-10 22:02 ?346次閱讀

    為什么Java線程沒(méi)有Running狀態(tài)

    的說(shuō)明: 一個(gè) JVM 執(zhí)行的線程處于這一狀態(tài)。(A thread executing in the
    的頭像 發(fā)表于 06-17 17:36 ?1421次閱讀

    Java線程池核心原理

    看過(guò)Java線程池源碼的小伙伴都知道,Java線程池中最核心的類就是ThreadPoolExecutor,
    的頭像 發(fā)表于 04-21 10:24 ?816次閱讀

    java實(shí)現(xiàn)多線程的幾種方式

    Java實(shí)現(xiàn)多線程的幾種方式 多線程是指程序包含了兩個(gè)或以上的線程,每個(gè)線程都可以并行執(zhí)行不同
    的頭像 發(fā)表于 03-14 16:55 ?550次閱讀