JVM線程模型概覽

HotSpot中的線程模型是Java線程(java.lang.Thread)與本地操作系統(tǒng)線程一一映射,本地線程在Java線程啟動(dòng)(調(diào)用start())時(shí)創(chuàng)建, 并在終止時(shí)回收。操作系統(tǒng)負(fù)責(zé)調(diào)度本地線程給可用的CPU來執(zhí)行趋艘。Java線程優(yōu)先級(jí)和操作系統(tǒng)優(yōu)先級(jí)之間的關(guān)系是相當(dāng)復(fù)雜的衡奥,并且因操作系統(tǒng)而異。

JVM線程類結(jié)構(gòu)

JVM中線程模型是相當(dāng)重要的一部分么抗,如執(zhí)行虛擬機(jī)指令的VM線程、執(zhí)行內(nèi)存垃圾回收的GC線程亚铁、采集虛擬機(jī)內(nèi)存及CPU狀況的監(jiān)控線程蝇刀, 還有專門用于執(zhí)行用戶線程的Java線程,如此多的線程之間是怎樣一種聯(lián)系和區(qū)別徘溢,我們一起來看一下吞琐。

JVM內(nèi)部主要的線程主要分為以下幾類:

// ==================================================
//              線程實(shí)現(xiàn)類結(jié)構(gòu)
// ==================================================

// Class hierarchy
// - Thread
//   - NamedThread               支持命名的非Java線程
//     - VMThread                   VM原始線程,用于執(zhí)行VM操作
//     - ConcurrentGCThread         并發(fā)GC線程
//     - WorkerThread               工作線程
//       - GangWorker               一組線程然爆,類似線程池
//       - GCTaskThread             GC任務(wù)線程
//   - JavaThread                C++層面的Java線程實(shí)現(xiàn)
//     - various subclasses eg CompilerThread, ServiceThread
//                                  各種子類站粟,如:編譯器線程,服務(wù)線程
//   - WatcherThread             監(jiān)視器線程曾雕,用于模擬計(jì)時(shí)器中斷

比較重要的還有OSThread操作系統(tǒng)層面的本地線程并且包含跟蹤線程狀態(tài)所需的附加操作系統(tǒng)級(jí)信息奴烙。OSThread還包含一個(gè)特定于平臺(tái)的“句柄”,用于標(biāo)識(shí)操作系統(tǒng)的實(shí)際線程

線程的創(chuàng)建與銷毀

在JVM內(nèi)部產(chǎn)生一個(gè)線程的基本方法有兩種 - 調(diào)用java.lang.Thread的start()方法 - 通過JNI attach到一個(gè)已經(jīng)存在的本地線程上

在java.lang.Thread啟動(dòng)時(shí),JVM會(huì)分別創(chuàng)建一個(gè)相關(guān)聯(lián)的JavaThread和OSThread對(duì)象切诀,最終創(chuàng)建本地線程揩环。 在準(zhǔn)備完所有的VM狀態(tài)(例如線程本地存儲(chǔ)和分配緩沖區(qū),同步對(duì)象等等)之后幅虑,本地線程啟動(dòng)丰滑。 本地線程完成初始化, 然后執(zhí)行啟動(dòng)方法倒庵,該方法導(dǎo)致執(zhí)行java.lang.Thread對(duì)象的run()方法褒墨,然后在返回時(shí)在處理任何未捕獲的異常之后 終止該線程,并且交互 與VM一起檢查此線程的終止是否需要終止整個(gè)VM擎宝。 線程終止釋放所有分配的資源貌亭,從一組已知線程中 刪除JavaThread,調(diào)用OSThread和JavaThread的析構(gòu)函數(shù)认臊,并最終在初始啟動(dòng)方法完成時(shí)停止執(zhí)行圃庭。

C++層面的JavaThread狀態(tài)

源碼位于src/hotspot/share/utilities/globalDefinitions.hpp

enum JavaThreadState {
  _thread_uninitialized     =  0, // should never happen (missing initialization)
  _thread_new               =  2, // just starting up, i.e., in process of being initialized
  _thread_new_trans         =  3, // corresponding transition state (not used, included for completness)
  _thread_in_native         =  4, // running in native code
  _thread_in_native_trans   =  5, // corresponding transition state
  _thread_in_vm             =  6, // running in VM
  _thread_in_vm_trans       =  7, // corresponding transition state
  _thread_in_Java           =  8, // running in Java or in stub code
  _thread_in_Java_trans     =  9, // corresponding transition state (not used, included for completness)
  _thread_blocked           = 10, // blocked in vm
  _thread_blocked_trans     = 11, // corresponding transition state
  _thread_max_state         = 12  // maximum thread state+1 - used for statistics allocation
};

最主要的有四個(gè): - _thread_new : 剛啟動(dòng)但還沒有初始化 - _thread_in_native : 在執(zhí)行本地代碼 - _thread_in_vm : 在執(zhí)行JVM本身的代碼 - _thread_in_Java : 在執(zhí)行解釋的或編譯的Java代碼

Java語言層面的線程狀態(tài)

源碼位于src/hotspot/share/classfile/javaClasses.hpp

這些線程狀態(tài)主要用于JVMTI和MANGEMENT狀態(tài)輸出。

enum ThreadStatus {
    NEW                      = 0,
    RUNNABLE                 = JVMTI_THREAD_STATE_ALIVE +          // runnable / running
                               JVMTI_THREAD_STATE_RUNNABLE,
    SLEEPING                 = JVMTI_THREAD_STATE_ALIVE +          // Thread.sleep()
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
                               JVMTI_THREAD_STATE_SLEEPING,
    IN_OBJECT_WAIT           = JVMTI_THREAD_STATE_ALIVE +          // Object.wait()
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_INDEFINITELY +
                               JVMTI_THREAD_STATE_IN_OBJECT_WAIT,
    IN_OBJECT_WAIT_TIMED     = JVMTI_THREAD_STATE_ALIVE +          // Object.wait(long)
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
                               JVMTI_THREAD_STATE_IN_OBJECT_WAIT,
    PARKED                   = JVMTI_THREAD_STATE_ALIVE +          // LockSupport.park()
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_INDEFINITELY +
                               JVMTI_THREAD_STATE_PARKED,
    PARKED_TIMED             = JVMTI_THREAD_STATE_ALIVE +          // LockSupport.park(long)
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
                               JVMTI_THREAD_STATE_PARKED,
    BLOCKED_ON_MONITOR_ENTER = JVMTI_THREAD_STATE_ALIVE +          // (re-)entering a synchronization block
                               JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER,
    TERMINATED               = JVMTI_THREAD_STATE_TERMINATED
  };

SLEEPING 調(diào)用Thread.sleep()進(jìn)入
IN_OBJECT_WAIT 調(diào)用Object.wait()進(jìn)入
IN_OBJECT_WAIT_TIMED 調(diào)用Object.wait(long)進(jìn)入
PARKED 調(diào)用LockSupport.park()進(jìn)入
PARKED_TIMED 調(diào)用LockSupport.park(long)進(jìn)入
Java線程狀態(tài)名稱

const char* java_lang_Thread::thread_status_name(oop java_thread) {
  assert(_thread_status_offset != 0, "Must have thread status");
  ThreadStatus status = (java_lang_Thread::ThreadStatus)java_thread->int_field(_thread_status_offset);
  switch (status) {
    case NEW                      : return "NEW";
    case RUNNABLE                 : return "RUNNABLE";
    case SLEEPING                 : return "TIMED_WAITING (sleeping)";
    case IN_OBJECT_WAIT           : return "WAITING (on object monitor)";
    case IN_OBJECT_WAIT_TIMED     : return "TIMED_WAITING (on object monitor)";
    case PARKED                   : return "WAITING (parking)";
    case PARKED_TIMED             : return "TIMED_WAITING (parking)";
    case BLOCKED_ON_MONITOR_ENTER : return "BLOCKED (on object monitor)";
    case TERMINATED               : return "TERMINATED";
    default                       : return "UNKNOWN";
  };
}

實(shí)例驗(yàn)證

隨便實(shí)現(xiàn)一個(gè)Java線程并在內(nèi)部sleep失晴,

public class Main {

    public static void main(String[] args) throws IOException {

        Thread t = new DemoThread();
        // 啟動(dòng)線程
        t.start();
        System.in.read();
    }
    
    static class DemoThread extends Thread {

        @Override
        public void run() {
            try {
                TimeUnit.HOURS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

然后通過jstack命令獲取其運(yùn)行狀態(tài):

$ jps
82005 Jps
81869 Main
81868 Launcher

$ jstack 81869
2018-07-02 00:28:52
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode):

"Attach Listener" #12 daemon prio=9 os_prio=31 tid=0x00007f99af000000 nid=0x1307 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007f99ae817000 nid=0x5a03 waiting on condition [0x0000700011471000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    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.workholiday.demoapp.Main$DemoThread.run(Main.java:30)

"Service Thread" #10 daemon prio=9 os_prio=31 tid=0x00007f99ae800800 nid=0x5603 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE


...


"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007f99ab814000 nid=0x3903 in Object.wait() [0x0000700010ad3000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076ab08ec8> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
    - locked <0x000000076ab08ec8> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"main" #1 prio=5 os_prio=31 tid=0x00007f99ae802000 nid=0x1c03 runnable [0x000070000ffb2000]
   java.lang.Thread.State: RUNNABLE
    at java.io.FileInputStream.readBytes(Native Method)
    at java.io.FileInputStream.read(FileInputStream.java:255)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
    - locked <0x000000076ab20660> (a java.io.BufferedInputStream)
    at com.workholiday.demoapp.Main.main(Main.java:19)

通過日志輸出會(huì)發(fā)現(xiàn)線程中的狀態(tài) - RUNNABLE - TIMED_WAITING (sleeping) - WAITING (on object monitor)

正是通過java_lang_Thread::thread_status_name()方法獲取的剧腻。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(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)容