源码分析:Java中的Thread的创建和运行

Posted dqVoice

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码分析:Java中的Thread的创建和运行相关的知识,希望对你有一定的参考价值。

注意:这篇文章代码较多,最好使用浏览器打开或点击原文去简书获取较好的阅读体验。

在面试候选人的时候,我有时候会提出这样的一个问题:说说你对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内的其他线程。

我们现在写一个简单的hello word程序,代码如下:

 
   
   
 
  1.    public class GcExample {

  2.        private static class E {

  3.            public static final int[] a = new int[1024 * 10];

  4.        }


  5.        public static void main(String[] args) {

  6.            System.out.println("hello world");

  7.            while (true) {

  8.                new E();

  9.            }

  10.        }

  11.    }

然后使用jmc(Java Mission Control)attach到这个程序上,展现为如下的情况:源码分析:Java中的Thread的创建和运行

  • 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,就可以得到如下内容:

 
   
   
 
  1. Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode):


  2. "RMI TCP Connection(5)-192.168.0.139" #17 daemon prio=9 os_prio=31 tid=0x00007fba7c830800 nid=0x5c03 runnable [0x000070000f740000]

  3.   java.lang.Thread.State: RUNNABLE

  4.    at java.net.SocketInputStream.socketRead0(Native Method)

  5.    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)

  6.    at java.net.SocketInputStream.read(SocketInputStream.java:171)

  7.    at java.net.SocketInputStream.read(SocketInputStream.java:141)

  8.    at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)

  9.    at java.io.BufferedInputStream.read(BufferedInputStream.java:265)

  10.    - locked <0x000000076f590708> (a java.io.BufferedInputStream)

  11.    at java.io.FilterInputStream.read(FilterInputStream.java:83)

  12.    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:550)

  13.    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)

  14.    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)

  15.    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$3/745402377.run(Unknown Source)

  16.    at java.security.AccessController.doPrivileged(Native Method)

  17.    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)

  18.    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

  19.    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

  20.    at java.lang.Thread.run(Thread.java:748)


  21. "RMI TCP Connection(2)-192.168.0.139" #16 daemon prio=9 os_prio=31 tid=0x00007fba7e0b0000 nid=0xa403 in Object.wait() [0x000070000f63c000]

  22.   java.lang.Thread.State: TIMED_WAITING (on object monitor)

  23.    at java.lang.Object.wait(Native Method)

  24.    - waiting on <0x000000076f580878> (a com.sun.jmx.remote.internal.ArrayNotificationBuffer)

  25.    at com.sun.jmx.remote.internal.ArrayNotificationBuffer.fetchNotifications(ArrayNotificationBuffer.java:449)

  26.    - locked <0x000000076f580878> (a com.sun.jmx.remote.internal.ArrayNotificationBuffer)

  27.    at com.sun.jmx.remote.internal.ArrayNotificationBuffer$ShareBuffer.fetchNotifications(ArrayNotificationBuffer.java:227)

  28.    at com.sun.jmx.remote.internal.ServerNotifForwarder.fetchNotifs(ServerNotifForwarder.java:274)

  29.    at javax.management.remote.rmi.RMIConnectionImpl$4.run(RMIConnectionImpl.java:1270)

  30.    at javax.management.remote.rmi.RMIConnectionImpl$4.run(RMIConnectionImpl.java:1268)

  31.    at javax.management.remote.rmi.RMIConnectionImpl.fetchNotifications(RMIConnectionImpl.java:1274)

  32.    at sun.reflect.GeneratedMethodAccessor62.invoke(Unknown Source)

  33.    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

  34.    at java.lang.reflect.Method.invoke(Method.java:498)

  35.    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)

  36.    at sun.rmi.transport.Transport$1.run(Transport.java:200)

  37.    at sun.rmi.transport.Transport$1.run(Transport.java:197)

  38.    at java.security.AccessController.doPrivileged(Native Method)

  39.    at sun.rmi.transport.Transport.serviceCall(Transport.java:196)

  40.    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)

  41.    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)

  42.    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)

  43.    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$3/745402377.run(Unknown Source)

  44.    at java.security.AccessController.doPrivileged(Native Method)

  45.    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)

  46.    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

  47.    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

  48.    at java.lang.Thread.run(Thread.java:748)


  49. "JMX server connection timeout 15" #15 daemon prio=9 os_prio=31 tid=0x00007fba80014800 nid=0xa503 in Object.wait() [0x000070000f53b000]

  50.   java.lang.Thread.State: TIMED_WAITING (on object monitor)

  51.    at java.lang.Object.wait(Native Method)

  52.    - waiting on <0x000000076f5887f8> (a [I)

  53.    at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)

  54.    - locked <0x000000076f5887f8> (a [I)

  55.    at java.lang.Thread.run(Thread.java:748)


  56. "RMI Scheduler(0)" #14 daemon prio=9 os_prio=31 tid=0x00007fba7c805800 nid=0xa603 waiting on condition [0x000070000f438000]

  57.   java.lang.Thread.State: TIMED_WAITING (parking)

  58.    at sun.misc.Unsafe.park(Native Method)

  59.    - parking to wait for  <0x000000076f598188> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)

  60.    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)

  61.    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)

  62.    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)

  63.    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)

  64.    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)

  65.    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)

  66.    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

  67.    at java.lang.Thread.run(Thread.java:748)


  68. "RMI TCP Accept-0" #12 daemon prio=9 os_prio=31 tid=0x00007fba7d906000 nid=0xa803 runnable [0x000070000f232000]

  69.   java.lang.Thread.State: RUNNABLE

  70.    at java.net.PlainSocketImpl.socketAccept(Native Method)

  71.    at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)

  72.    at java.net.ServerSocket.implAccept(ServerSocket.java:545)

  73.    at java.net.ServerSocket.accept(ServerSocket.java:513)

  74.    at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:52)

  75.    at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:400)

  76.    at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:372)

  77.    at java.lang.Thread.run(Thread.java:748)


  78. "Attach Listener" #10 daemon prio=9 os_prio=31 tid=0x00007fba7d865800 nid=0xa903 waiting on condition [0x0000000000000000]

  79.   java.lang.Thread.State: RUNNABLE


  80. "Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007fba7d803000 nid=0x3903 runnable [0x0000000000000000]

  81.   java.lang.Thread.State: RUNNABLE


  82. "C1 CompilerThread3" #8 daemon prio=9 os_prio=31 tid=0x00007fba7e002000 nid=0x3803 waiting on condition [0x0000000000000000]

  83.   java.lang.Thread.State: RUNNABLE


  84. "C2 CompilerThread2" #7 daemon prio=9 os_prio=31 tid=0x00007fba80000000 nid=0x3703 waiting on condition [0x0000000000000000]

  85.   java.lang.Thread.State: RUNNABLE


  86. "C2 CompilerThread1" #6 daemon prio=9 os_prio=31 tid=0x00007fba7d82d800 nid=0x3e03 waiting on condition [0x0000000000000000]

  87.   java.lang.Thread.State: RUNNABLE


  88. "C2 CompilerThread0" #5 daemon prio=9 os_prio=31 tid=0x00007fba7c020000 nid=0x3f03 waiting on condition [0x0000000000000000]

  89.   java.lang.Thread.State: RUNNABLE


  90. "Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fba7c01e800 nid=0x3403 runnable [0x0000000000000000]

  91.   java.lang.Thread.State: RUNNABLE


  92. "Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fba7f022000 nid=0x4903 in Object.wait() [0x000070000e917000]

  93.   java.lang.Thread.State: WAITING (on object monitor)

  94.    at java.lang.Object.wait(Native Method)

  95.    - waiting on <0x000000076f5a0600> (a java.lang.ref.ReferenceQueue$Lock)

  96.    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)

  97.    - locked <0x000000076f5a0600> (a java.lang.ref.ReferenceQueue$Lock)

  98.    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)

  99.    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)


  100. "Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fba7c01d800 nid=0x4b03 in Object.wait() [0x000070000e814000]

  101.   java.lang.Thread.State: WAITING (on object monitor)

  102.    at java.lang.Object.wait(Native Method)

  103.    - waiting on <0x000000076f5983e8> (a java.lang.ref.Reference$Lock)

  104.    at java.lang.Object.wait(Object.java:502)

  105.    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)

  106.    - locked <0x000000076f5983e8> (a java.lang.ref.Reference$Lock)

  107.    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)


  108. "main" #1 prio=5 os_prio=31 tid=0x00007fba7f000000 nid=0x2303 runnable [0x000070000ddf6000]

  109.   java.lang.Thread.State: RUNNABLE

  110.    at GcExample.main(GcExample.java:9)


  111. "VM Thread" os_prio=31 tid=0x00007fba7c01b000 nid=0x2f03 runnable


  112. "GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fba7c007000 nid=0x2007 runnable


  113. "GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fba7d804800 nid=0x2a03 runnable


  114. "GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fba7c007800 nid=0x5303 runnable


  115. "GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fba7f800000 nid=0x2c03 runnable


  116. "GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007fba7d805000 nid=0x5103 runnable


  117. "GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007fba7c013800 nid=0x5003 runnable


  118. "GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007fba7f001000 nid=0x4e03 runnable


  119. "GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007fba7c014000 nid=0x4c03 runnable


  120. "VM Periodic Task Thread" os_prio=31 tid=0x00007fba7d858800 nid=0x3a03 waiting on condition


  121. 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()方法的内容如下:

 
   
   
 
  1.    /**

  2.     * Causes this thread to begin execution; the Java Virtual Machine

  3.     * calls the <code>run</code> method of this thread.

  4.     * <p>

  5.     * The result is that two threads are running concurrently: the

  6.     * current thread (which returns from the call to the

  7.     * <code>start</code> method) and the other thread (which executes its

  8.     * <code>run</code> method).

  9.     * <p>

  10.     * It is never legal to start a thread more than once.

  11.     * In particular, a thread may not be restarted once it has completed

  12.     * execution.

  13.     *

  14.     * @exception  IllegalThreadStateException  if the thread was already

  15.     *               started.

  16.     * @see        #run()

  17.     * @see        #stop()

  18.     */

  19.    public synchronized void start() {

  20.        /**

  21.         * This method is not invoked for the main method thread or "system"

  22.         * group threads created/set up by the VM. Any new functionality added

  23.         * to this method in the future may have to also be added to the VM.

  24.         *

  25.         * A zero status value corresponds to state "NEW".

  26.         */

  27.        if (threadStatus != 0)

  28.            throw new IllegalThreadStateException();


  29.        /* Notify the group that this thread is about to be started

  30.         * so that it can be added to the group's list of threads

  31.         * and the group's unstarted count can be decremented. */

  32.        group.add(this);


  33.        boolean started = false;

  34.        try {

  35.            start0();

  36.            started = true;

  37.        } finally {

  38.            try {

  39.                if (!started) {

  40.                    group.threadStartFailed(this);

  41.                }

  42.            } catch (Throwable ignore) {

  43.                /* do nothing. If start0 threw a Throwable then

  44.                  it will be passed up the call stack */

  45.            }

  46.        }

  47.    }


  48.    private native void start0();

根据注释中说的,一个线程退出后是再次start是非法的,会抛出异常,我们可以用下面的代码验证下:

 
   
   
 
  1. package org.java.learn.concurrent;


  2. public class ThreadRestartExample {


  3.    public static void main(String[] args) throws InterruptedException {

  4.        Thread thread = new Thread(() -> {

  5.            System.out.println("hello");

  6.        });


  7.        thread.start();


  8.        Thread.sleep(1000);


  9.        thread.start();

  10.    }

  11. }

运行这个代码的结果是:

 
   
   
 
  1. hello

  2. Exception in thread "main" java.lang.IllegalThreadStateException

  3.    at java.lang.Thread.start(Thread.java:708)

  4.    at org.java.learn.concurrent.ThreadRestartExample.main(ThreadRestartExample.java:18)


  5. Process finished with exit code 1

start方法调用了start0方法,这是一个JNI接口,在Java中通过JNI接口可以实现Java调用本地方法;通过JVMTI接口可以实现在C++空间调用Java对象的方法。start0方法的实现在 jdk/src/share/native/java/lang/Thread.c中定义,代码如下所示:

 
   
   
 
  1. static JNINativeMethod methods[] = {

  2.    {"start0",           "()V",        (void *)&JVM_StartThread},

  3.    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},

  4.    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},

  5.    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},

  6.    {"resume0",          "()V",        (void *)&JVM_ResumeThread},

  7.    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},

  8.    {"yield",            "()V",        (void *)&JVM_Yield},

  9.    {"sleep",            "(J)V",       (void *)&JVM_Sleep},

  10.    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},

  11.    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},

  12.    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},

  13.    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},

  14.    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},

  15.    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},

  16.    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},

  17.    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},

  18. };

JVM_StartThread的接口定义在jvm.h中,JDK中用到的jni接口,最终都会在jvm.h文件中定义,并在jvm.cpp中作为C++实现的入口,也就是说jvm.cpp是Java世界和JVM中C++世界沟通的桥梁。

 
   
   
 
  1. /*

  2. * java.lang.Thread

  3. */

  4. JNIEXPORT void JNICALL

  5. JVM_StartThread(JNIEnv *env, jobject thread);

JVM_StartThread的具体实现在jvm.cpp中,主要代码逻辑列举如下(本文主要是要看线程创建的逻辑,因此一些分支代码没有展示):

 
   
   
 
  1. JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))

  2.  JVMWrapper("JVM_StartThread");

  3.  JavaThread *native_thread = NULL;

  4.  //……

  5.  //获取栈的大小

  6.  jlong size =     java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));


  7.  //……

  8.  //栈的大小不能为负数

  9.  size_t sz = size > 0 ? (size_t) size : 0;

  10.  //通过new JavaThread新建os线程对象,这里thread_entry就是runnable的run方法。

  11.  native_thread = new JavaThread(&thread_entry, sz);


  12.  //……

  13.  Thread::start(native_thread);


  14. JVM_END

JavaThread的构造方法实现时在thread.cpp文件中,做一些准备工作后,会通过 os::create_thread(this,thr_type,stack_sz);创建线程,os::create_thread的实现时跟具体平台有关的,如下图所示:源码分析:Java中的Thread的创建和运行

这里我们选择oslinux.cpp这个文件。os::createthread的主要动作有几个:

  1. 通过 pthread_attr_init(&attr);初始化线程的属性

  2. 通过 intret=pthread_create(&tid,&attr,(void*(*)(void*))java_start,thread);创建os线程,这里最重要了,参见pthread_create手册,可以知道,第三个参数表示启动这个线程后要执行的方法的入口,第四个参数表示要给这个方法传入的参数。

这里我们看下java_start方法的实现(该方法也在thread.cpp中),在这个方法的入参是Thread指针;

 
   
   
 
  1. // Thread start routine for all newly created threads

  2. static void *java_start(Thread *thread) {

  3.  // Try to randomize the cache line index of hot stack frames.

  4.  // This helps when threads of the same stack traces evict each other's

  5.  // cache lines. The threads can be either from the same JVM instance, or

  6.  // from different JVM instances. The benefit is especially true for

  7.  // processors with hyperthreading technology.

  8.  static int counter = 0;

  9.  int pid = os::current_process_id();

  10.  alloca(((pid ^ counter++) & 7) * 128);


  11.  ThreadLocalStorage::set_thread(thread);


  12.  OSThread* osthread = thread->osthread();

  13.  Monitor* sync = osthread->startThread_lock();


  14.  // non floating stack LinuxThreads needs extra check, see above

  15.  if (!_thread_safety_check(thread)) {

  16.    // notify parent thread

  17.    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);

  18.    osthread->set_state(ZOMBIE);

  19.    sync->notify_all();

  20.    return NULL;

  21.  }


  22.  // thread_id is kernel thread id (similar to Solaris LWP id)

  23.  osthread->set_thread_id(os::Linux::gettid());


  24.  if (UseNUMA) {

  25.    int lgrp_id = os::numa_get_group_id();

  26.    if (lgrp_id != -1) {

  27.      thread->set_lgrp_id(lgrp_id);

  28.    }

  29.  }

  30.  // initialize signal mask for this thread

  31.  os::Linux::hotspot_sigmask(thread);


  32.  // initialize floating point control register

  33.  os::Linux::init_thread_fpu_state();


  34.  // handshaking with parent thread

  35.  {

  36.    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);


  37.    // notify parent thread

  38.    osthread->set_state(INITIALIZED);

  39.    sync->notify_all();


  40.    // wait until os::start_thread()

  41.    // 这里说明,新创建的os线程不会立即执行,会等os::start_thread()的通知,在后面我们马上会分析到。

  42.    while (osthread->get_state() == INITIALIZED) {

  43.      sync->wait(Mutex::_no_safepoint_check_flag);

  44.    }

  45.  }


  46.  // call one more level start routine

  47.  thread->run();


  48.  return 0;

  49. }

在方法的最后,会通过 thread->run();调用JavaThread的run方法,然后再到JavaThread的threadmaininner方法,

 
   
   
 
  1. void JavaThread::thread_main_inner() {

  2.  assert(JavaThread::current() == this, "sanity check");

  3.  assert(this->threadObj() != NULL, "just checking");


  4.  // Execute thread entry point unless this thread has a pending exception

  5.  // or has been stopped before starting.

  6.  // Note: Due to JVM_StopThread we can have pending exceptions already!

  7.  if (!this->has_pending_exception() &&

  8.      !java_lang_Thread::is_stillborn(this->threadObj())) {

  9.    {

  10.      ResourceMark rm(this);

  11.      this->set_native_thread_name(this->get_thread_name());

  12.    }

  13.    HandleMark hm(this);

  14.    //注意:这里就是Java线程要执行的run方法

  15.    this->entry_point()(this, this);

  16.  }


  17.  DTRACE_THREAD_PROBE(stop, this);


  18.  this->exit(false);

  19.  delete this;

  20. }

this->entry_point()(this,this);这行的调用,就会执行java.lang.Thread中的run方法,那么这个entrypoint是在哪里被设置到JavaThread对象中的呢,回顾上文,在jvm.cpp里有一个new JavaThread(&threadentry,sz)的调用,是的,就是这里,thread_entry的具体实现是:

 
   
   
 
  1. static void thread_entry(JavaThread* thread, TRAPS) {

  2.  HandleMark hm(THREAD);

  3.  Handle obj(THREAD, thread->threadObj());

  4.  JavaValue result(T_VOID);

  5.  JavaCalls::call_virtual(&result,

  6.                          obj,

  7.                          KlassHandle(THREAD, SystemDictionary::Thread_klass()),

  8.                          vmSymbols::run_method_name(),

  9.                          vmSymbols::void_method_signature(),

  10.                          THREAD);

  11. }

这段代码要做的事情就是在JVM的c++世界里,获取到对应的java.lang.Thread的对象,然后调用它的run方法。

再看下JVMStartThread的逻辑,nativethread被创建后并不会立即被执行,而是出于初始化状态,后面还会执行 Thread::start(native_thread);代码,这是做了什么工作呢?

 
   
   
 
  1. void Thread::start(Thread* thread) {

  2.  trace("start", thread);

  3.  // Start is different from resume in that its safety is guaranteed by context or

  4.  // being called from a Java method synchronized on the Thread object.

  5.  if (!DisableStartThread) {

  6.    if (thread->is_Java_thread()) {

  7.      // Initialize the thread state to RUNNABLE before starting this thread.

  8.      // Can not set it after the thread started because we do not know the

  9.      // exact thread state at that time. It could be in MONITOR_WAIT or

  10.      // in SLEEPING or some other state.

  11.      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),

  12.                                          java_lang_Thread::RUNNABLE);

  13.    }

  14.    os::start_thread(thread);

  15.  }

  16. }

根据代码可知道,这个方法先将thread的状态设置为RUNNABLE,然后再调用 os::start_thread(thread);通知刚刚创建的os线程开始运行,具体的代码如下:

 
   
   
 
  1. void os::start_thread(Thread* thread) {

  2.  // guard suspend/resume

  3.  MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);

  4.  OSThread* osthread = thread->osthread();

  5.  osthread->set_state(RUNNABLE);

  6.  pd_start_thread(thread);

  7. }

在这里pdstartthread(具体实现在os_linux.cpp)就负责通知刚刚被创建的但是处于初始化状态的线程,代码如下:

 
   
   
 
  1. void os::pd_start_thread(Thread* thread) {

  2.  OSThread * osthread = thread->osthread();

  3.  assert(osthread->get_state() != INITIALIZED, "just checking");

  4.  Monitor* sync_with_child = osthread->startThread_lock();

  5.  MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);

  6.  sync_with_child->notify();

  7. }

sync_with_child->notify();这行代码就是用来通知线程开始运行的。

总结

这篇文章主要梳理了三个问题:(1)JVM中的线程模型是怎么样的,跟os中的线程一一对应;(2)JVM里常见的几类线程都有哪些?VM Thread、周期线程、Compiler 线程、GC线程、信号量处理线程;(3)当我们在java代码中执行start()方法的时候,JVM内部做了哪些事情?

参考资料

  1. HotSpot JVM internal threads

  2. JVM原理与实现——Thread

  3. How Java thread maps to OS thread?

  4. Java并发的官方文档

  5. JVM attach机制的实现

  6. HOTSPOTVM线程实现浅析


动动手指,关注一下啦


以上是关于源码分析:Java中的Thread的创建和运行的主要内容,如果未能解决你的问题,请参考以下文章

如何分析Thread Dump

干货:Java多线程详解(内附源码)

JDK1.8 Thread 源码分析

【Poco笔记】线程Thread

java Thread源码分析

java Thread源码分析