在面试候选人的时候, 我有时候会提出这样的一个问题: 说说你对 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 内的其他线程.
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 到这个程序上, 展现为如下的情况:
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 <code>run</code> method of this thread.
- * <p>
- * The result is that two threads are running concurrently: the
- * current thread (which returns from the call to the
- * <code>start</code> method) and the other thread (which executes its
- * <code>run</code> method).
- * <p>
- * 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 的实现时跟具体平台有关的, 如下图所示:
os::create_thread 有不同平台的实现
这里我们选择 os_linux.cpp 这个文件. os::create_thread 的主要动作有几个:
通过
pthread_attr_init(&attr);
初始化线程的属性
通过
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
创建 os 线程, 这里最重要了, 参见手册, 可以知道, 第三个参数表示启动这个线程后要执行的方法的入口, 第四个参数表示要给这个方法传入的参数.
这里我们看下 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 内部做了哪些事情?
参考资料
HotSpot JVM internal threads
JVM 原理与实现 --Thread
How Java thread maps to OS thread?
Java 并发的官方文档
JVM attach 机制的实现 http://lovestblog.cn/blog/2014/06/18/jvm-attach/
HOTSPOTVM 线程实现浅析
来源: http://www.jianshu.com/p/71290d20b4ff