Java線程與內(nèi)核線程
本篇文章探究下Java線程與內(nèi)核線程的關(guān)系.
在Java中,一個Java的線程對應(yīng)一個內(nèi)核的線程,實際的業(yè)務(wù)代碼是由內(nèi)核線程來執(zhí)行的,而Java線程只是一個傀儡.
先通過一個簡單的實驗熱熱身
import java.lang.Thread;public class Example {public static void main(String[] args) throws Exception {new Thread(new Runnable() {public void run() {try {Thread.sleep(20000);//休眠20秒} catch(Exception x) {}System.out.println("Thread-A");}}, "Thread-A").start();Thread.sleep(60000);System.out.println("main");}}
以上代碼,主要就是要讓Thread-A線程先退出,然后JVM再退出.
觀察下Thread-A線程退出之后,對應(yīng)的內(nèi)核線程是否也退出了.
為了觀察現(xiàn)象,使用到一個JDK自帶的jvisualvm圖形化工具和ps命令.

編譯并運行

首先使用jvisualvm觀察下線程的情況

說明: 進程ID=686
Thread-A線程在運行完成之后,就退出了,這里看到的線程是Java層面的線程,那么我們通過ps命令看下內(nèi)核層面的線程情況.
分別在Thread-A線程運行中和運行完成之后,通過ps -Lf 命令查看下線程.

在Thread-A線程結(jié)束之后,對應(yīng)的有個內(nèi)核線程707也消失了,那么這個內(nèi)核線程707是不是就是對應(yīng)Java的Thread-A線程呢?
我們是使用strace -ff -o out java Example命令運行的程序,因此它會打印系統(tǒng)調(diào)用相關(guān)的信息.

707內(nèi)核線程打印了Thread-A, 也就是說,內(nèi)核線程707對應(yīng)Java的Thread-A線程.
【總結(jié)】 當Java的線程退出之后,對應(yīng)的內(nèi)核線程也會退出.
當然,以上是我們根據(jù)現(xiàn)象觀察出來的結(jié)果,那么接下來我們通過查看JVM源碼看一下.

在Java中調(diào)用start方法啟動線程, 底層映射到JVM中的JVM_StartThread方法.

接下來繼續(xù)調(diào)用創(chuàng)建邏輯.

調(diào)用os::create_thread(this, thr_type, stack_sz)繼續(xù)創(chuàng)建線程邏輯.

底層調(diào)用C庫的pthread_create創(chuàng)建內(nèi)核線程.
創(chuàng)建完成之后, 子線程執(zhí)行java_start方法,而父線程暫時阻塞住.

子線程喚醒父線程,然后子線程阻塞住.
父線程被喚醒之后,執(zhí)行start方法.


父線程喚醒之前阻塞的子線程

子線程被喚醒之后,執(zhí)行JVM中線程的run方法


最后子線程會調(diào)用執(zhí)行Java線程的run方法.
同時當Java線程的run方法執(zhí)行完成之后, 線程就調(diào)用exit退出了. 這里也就解釋了Java線程退出之后,內(nèi)核線程也會退出的原因了.
這里附一張全貌圖

總結(jié)一下就是父線程創(chuàng)建了子線程, 子線程執(zhí)行完成之后,子線程就自動退出了.
為了創(chuàng)建線程,對于我們JDK層面就是一個Thread類對象,但是其實JVM內(nèi)部還涉及幾個線程對象.比如JavaThread, OSThread

包括你看到的JDK層面的Thread的線程狀態(tài),和JVM中的線程狀態(tài),以及內(nèi)核的線程狀態(tài),都是不完全一樣的.

以上也只是分析了一個普通的線程退出之后,內(nèi)核線程也自然退出了.
難道m(xù)ain線程也是這樣的嗎? main線程是第一個線程嗎?
我們后面再單獨說下main線程的情況.
