作者:杜琪
鏈接:https://www.jianshu.com/p/71290d20b4ff

在面試候選人的時候,我有時候會提出這樣的一個問題:說說你對Java線程的理解?從這個問題開始,可以考察候選人對併發基礎的掌握、對操作系統基本概念的理解,如果遇到對底層有濃厚興趣的同學,我會拋出另一個問題:Java裏的線程和操作系統的線程是什麼關係?它們是如何對應的?這兩個問題,就是今天這篇文章想講述的。

基礎知識

JVM中的線程是和OS中的線程一一對應的,操作系統負責調度所有的線程,因此在不同的平臺上,Java線程的優先級有所不同。

在JVM中除了應用線程,還有其他的一些線程用於支持JVM的運行,這些線程可以被劃分爲以下幾類:

  • VM Thread:負責JVM在安全點內的各種操作,這些操作(諸如自動內存管理、取消偏向鎖、線程dump、線程掛起等等)在執行過程中需要JVM處於這樣一個狀態——堆的內容不會被改變,這種狀態在JVM裏叫做安全點(safe-point)。
  • Periodic task thread:這個線程負責響應定時觸發的事件(例如:中斷),用來執行一些定時操作。
  • GC thread:這些線程負責JVM裏的垃圾收集活動;
  • Compiler threads:這些線程負責在運行時將字節碼編譯爲本地代碼;
  • Singal dispatcher thread:這些線程負責響應外部發給當前JVM進程的信號,並通過調用JVM內的其他線程。
源碼分析:Java中的Thread的創建和運行

JVM中的線程模型

我們現在寫一個簡單的hello word程序,代碼如下:

 public class GcExample {
private static class E {
public static final int[] a = new int[1024 * 10];
}
public static void main(String[] args) {
System.out.println("hello world");
while (true) {
new E();
}
}
}

然後使用jmc(Java Mission Control)attach到這個程序上,展現爲如下的情況:

源碼分析:Java中的Thread的創建和運行

jmc中看到的線程

  • RMI開頭的線程,負責JVM跟JMC客戶端通信,吐出JVM內的運行信息;
  • Attach Listener和Single Dispatcher兩個線程,屬於信號處理線程,負責接收外部到當前JVM的attach信號,並建立通信用的文件socket;
  • Finalizer線程,用於處理Finalizer隊列的線程,在Java中,如果一個對象重寫了finalize()方法,那麼JVM會爲之創建一個對應的Finalizer對象,所有的Finzlizer對象會構成一個列表,由Finalizer線程統一處理
  • Reference Handler,負責JVM中的引用處理
  • main,我們例子中的業務線程。

我想你現在也有這個疑問——跟上面說的那個分類對不上,有些線程沒看到,是的,可能是由於JMC的實現機制,這些線程沒有被展示出來,我們再用jstack命令做一次線程dump,就可以得到如下內容:

Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode):
"RMI TCP Connection(5)-192.168.0.139" #17 daemon prio=9 os_prio=31 tid=0x00007fba7c830800 nid=0x5c03 runnable [0x000070000f740000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x000000076f590708> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:550)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$3/745402377.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"RMI TCP Connection(2)-192.168.0.139" #16 daemon prio=9 os_prio=31 tid=0x00007fba7e0b0000 nid=0xa403 in Object.wait() [0x000070000f63c000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076f580878> (a com.sun.jmx.remote.internal.ArrayNotificationBuffer)
at com.sun.jmx.remote.internal.ArrayNotificationBuffer.fetchNotifications(ArrayNotificationBuffer.java:449)
- locked <0x000000076f580878> (a com.sun.jmx.remote.internal.ArrayNotificationBuffer)
at com.sun.jmx.remote.internal.ArrayNotificationBuffer$ShareBuffer.fetchNotifications(ArrayNotificationBuffer.java:227)
at com.sun.jmx.remote.internal.ServerNotifForwarder.fetchNotifs(ServerNotifForwarder.java:274)
at javax.management.remote.rmi.RMIConnectionImpl$4.run(RMIConnectionImpl.java:1270)
at javax.management.remote.rmi.RMIConnectionImpl$4.run(RMIConnectionImpl.java:1268)
at javax.management.remote.rmi.RMIConnectionImpl.fetchNotifications(RMIConnectionImpl.java:1274)
at sun.reflect.GeneratedMethodAccessor62.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$3/745402377.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"JMX server connection timeout 15" #15 daemon prio=9 os_prio=31 tid=0x00007fba80014800 nid=0xa503 in Object.wait() [0x000070000f53b000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076f5887f8> (a [I)
at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)
- locked <0x000000076f5887f8> (a [I)
at java.lang.Thread.run(Thread.java:748)
"RMI Scheduler(0)" #14 daemon prio=9 os_prio=31 tid=0x00007fba7c805800 nid=0xa603 waiting on condition [0x000070000f438000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076f598188> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"RMI TCP Accept-0" #12 daemon prio=9 os_prio=31 tid=0x00007fba7d906000 nid=0xa803 runnable [0x000070000f232000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:52)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:400)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:372)
at java.lang.Thread.run(Thread.java:748)
"Attach Listener" #10 daemon prio=9 os_prio=31 tid=0x00007fba7d865800 nid=0xa903 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007fba7d803000 nid=0x3903 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #8 daemon prio=9 os_prio=31 tid=0x00007fba7e002000 nid=0x3803 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #7 daemon prio=9 os_prio=31 tid=0x00007fba80000000 nid=0x3703 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #6 daemon prio=9 os_prio=31 tid=0x00007fba7d82d800 nid=0x3e03 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=31 tid=0x00007fba7c020000 nid=0x3f03 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fba7c01e800 nid=0x3403 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fba7f022000 nid=0x4903 in Object.wait() [0x000070000e917000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076f5a0600> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x000000076f5a0600> (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)
"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fba7c01d800 nid=0x4b03 in Object.wait() [0x000070000e814000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076f5983e8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076f5983e8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=31 tid=0x00007fba7f000000 nid=0x2303 runnable [0x000070000ddf6000]
java.lang.Thread.State: RUNNABLE
at GcExample.main(GcExample.java:9)
"VM Thread" os_prio=31 tid=0x00007fba7c01b000 nid=0x2f03 runnable
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fba7c007000 nid=0x2007 runnable
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fba7d804800 nid=0x2a03 runnable
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fba7c007800 nid=0x5303 runnable
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fba7f800000 nid=0x2c03 runnable
"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007fba7d805000 nid=0x5103 runnable
"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007fba7c013800 nid=0x5003 runnable
"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007fba7f001000 nid=0x4e03 runnable
"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007fba7c014000 nid=0x4c03 runnable
"VM Periodic Task Thread" os_prio=31 tid=0x00007fba7d858800 nid=0x3a03 waiting on condition
JNI global references: 295

OK,從上面這個dump文件中,可以找到Periodic Task Thread、GC Thread、VM Thread、Compiler Thread的身影了。

JVM源碼分析

前面從概念和分類兩個角度觀察了JVM中的線程,現在我們從源碼角度看下另一個問題,JVM是如何實現Java線程的。

java.lang.Thread類的start接口,用來啓動一個Java線程,然後JVM會執行run()方法中的內容,run()方法是Runnable接口定義然後在java.lang.Thread中提供了實現方法,start()方法的內容如下:

 /**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the run method of this thread.
*


* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* start method) and the other thread (which executes its
* run method).
*


* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();

根據註釋中說的,一個線程退出後是再次start是非法的,會拋出異常,我們可以用下面的代碼驗證下:

package org.java.learn.concurrent;
public class ThreadRestartExample {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("hello");
});
thread.start();
Thread.sleep(1000);
thread.start();
}
}

運行這個代碼的結果是:

hello
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at org.java.learn.concurrent.ThreadRestartExample.main(ThreadRestartExample.java:18)
Process finished with exit code 1

start方法調用了start0方法,這是一個JNI接口,在Java中通過JNI接口可以實現Java調用本地方法;通過JVMTI接口可以實現在C++空間調用Java對象的方法。start0方法的實現在jdk/src/share/native/java/lang/Thread.c中定義,代碼如下所示:

static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield", "()V", (void *)&JVM_Yield},
{"sleep", "(J)V", (void *)&JVM_Sleep},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"countStackFrames", "()I", (void *)&JVM_CountStackFrames},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

JVM_StartThread的接口定義在jvm.h中,JDK中用到的jni接口,最終都會在jvm.h文件中定義,並在jvm.cpp中作爲C++實現的入口,也就是說jvm.cpp是Java世界和JVM中C++世界溝通的橋樑。

/*
* java.lang.Thread
*/
JNIEXPORT void JNICALL
JVM_StartThread(JNIEnv *env, jobject thread);

JVM_StartThread的具體實現在jvm.cpp中,主要代碼邏輯列舉如下(本文主要是要看線程創建的邏輯,因此一些分支代碼沒有展示):

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
//……
//獲取棧的大小
jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));

//……
//棧的大小不能爲負數
size_t sz = size > 0 ? (size_t) size : 0;
//通過new JavaThread新建os線程對象,這裏thread_entry就是runnable的run方法。
native_thread = new JavaThread(&thread_entry, sz);

//……
Thread::start(native_thread);
JVM_END

JavaThread的構造方法實現時在thread.cpp文件中,做一些準備工作後,會通過os::create_thread(this, thr_type, stack_sz);創建線程,os::create_thread的實現時跟具體平臺有關的,如下圖所示:

源碼分析:Java中的Thread的創建和運行

os::create_thread有不同平臺的實現

這裏我們選擇os_linux.cpp這個文件。os::create_thread的主要動作有幾個:

  1. 通過pthread_attr_init(&attr);初始化線程的屬性
  2. 通過int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);創建os線程,這裏最重要了,參見pthread_create手冊,可以知道,第三個參數表示啓動這個線程後要執行的方法的入口,第四個參數表示要給這個方法傳入的參數。

這裏我們看下java_start方法的實現(該方法也在thread.cpp中),在這個方法的入參是Thread指針;

// Thread start routine for all newly created threads
static void *java_start(Thread *thread) {
// Try to randomize the cache line index of hot stack frames.
// This helps when threads of the same stack traces evict each other's
// cache lines. The threads can be either from the same JVM instance, or
// from different JVM instances. The benefit is especially true for
// processors with hyperthreading technology.
static int counter = 0;
int pid = os::current_process_id();
alloca(((pid ^ counter++) & 7) * 128);
ThreadLocalStorage::set_thread(thread);
OSThread* osthread = thread->osthread();
Monitor* sync = osthread->startThread_lock();
// non floating stack LinuxThreads needs extra check, see above
if (!_thread_safety_check(thread)) {
// notify parent thread
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
osthread->set_state(ZOMBIE);
sync->notify_all();
return NULL;
}
// thread_id is kernel thread id (similar to Solaris LWP id)
osthread->set_thread_id(os::Linux::gettid());
if (UseNUMA) {
int lgrp_id = os::numa_get_group_id();
if (lgrp_id != -1) {
thread->set_lgrp_id(lgrp_id);
}
}
// initialize signal mask for this thread
os::Linux::hotspot_sigmask(thread);
// initialize floating point control register
os::Linux::init_thread_fpu_state();
// handshaking with parent thread
{
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
// notify parent thread
osthread->set_state(INITIALIZED);
sync->notify_all();
// wait until os::start_thread()
// 這裏說明,新創建的os線程不會立即執行,會等os::start_thread()的通知,在後面我們馬上會分析到。
while (osthread->get_state() == INITIALIZED) {
sync->wait(Mutex::_no_safepoint_check_flag);
}
}
// call one more level start routine
thread->run();
return 0;
}

在方法的最後,會通過thread->run();調用JavaThread的run方法,然後再到JavaThread的thread_main_inner方法,

void JavaThread::thread_main_inner() {
assert(JavaThread::current() == this, "sanity check");
assert(this->threadObj() != NULL, "just checking");
// Execute thread entry point unless this thread has a pending exception
// or has been stopped before starting.
// Note: Due to JVM_StopThread we can have pending exceptions already!
if (!this->has_pending_exception() &&
!java_lang_Thread::is_stillborn(this->threadObj())) {
{
ResourceMark rm(this);
this->set_native_thread_name(this->get_thread_name());
}
HandleMark hm(this);
//注意:這裏就是Java線程要執行的run方法
this->entry_point()(this, this);
}
DTRACE_THREAD_PROBE(stop, this);
this->exit(false);
delete this;
}

this->entry_point()(this, this);這行的調用,就會執行java.lang.Thread中的run方法,那麼這個entry_point是在哪裏被設置到JavaThread對象中的呢,回顧上文,在jvm.cpp裏有一個new JavaThread(&thread_entry,sz)的調用,是的,就是這裏,thread_entry的具體實現是:

static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,
obj,
KlassHandle(THREAD, SystemDictionary::Thread_klass()),
vmSymbols::run_method_name(),
vmSymbols::void_method_signature(),
THREAD);
}

這段代碼要做的事情就是在JVM的c++世界裏,獲取到對應的java.lang.Thread的對象,然後調用它的run方法。

再看下JVM_StartThread的邏輯,native_thread被創建後並不會立即被執行,而是出於初始化狀態,後面還會執行Thread::start(native_thread);代碼,這是做了什麼工作呢?

void Thread::start(Thread* thread) {
trace("start", thread);
// Start is different from resume in that its safety is guaranteed by context or
// being called from a Java method synchronized on the Thread object.
if (!DisableStartThread) {
if (thread->is_Java_thread()) {
// Initialize the thread state to RUNNABLE before starting this thread.
// Can not set it after the thread started because we do not know the
// exact thread state at that time. It could be in MONITOR_WAIT or
// in SLEEPING or some other state.
java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
java_lang_Thread::RUNNABLE);
}
os::start_thread(thread);
}
}

根據代碼可知道,這個方法先將thread的狀態設置爲RUNNABLE,然後再調用os::start_thread(thread);通知剛剛創建的os線程開始運行,具體的代碼如下:

void os::start_thread(Thread* thread) {
// guard suspend/resume
MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
OSThread* osthread = thread->osthread();
osthread->set_state(RUNNABLE);
pd_start_thread(thread);
}

在這裏pd_start_thread(具體實現在os_linux.cpp)就負責通知剛剛被創建的但是處於初始化狀態的線程,代碼如下:

void os::pd_start_thread(Thread* thread) {
OSThread * osthread = thread->osthread();
assert(osthread->get_state() != INITIALIZED, "just checking");
Monitor* sync_with_child = osthread->startThread_lock();
MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
sync_with_child->notify();
}

sync_with_child->notify();這行代碼就是用來通知線程開始運行的。

總結

這篇文章主要梳理了三個問題:(1)JVM中的線程模型是怎麼樣的,跟os中的線程一一對應;(2)JVM裏常見的幾類線程都有哪些?VM Thread、週期線程、Compiler 線程、GC線程、信號量處理線程;(3)當我們在java代碼中執行start()方法的時候,JVM內部做了哪些事情?

相關文章