一、线程启动分析

  1. new Thread(() -> {
  2. // todo
  3. }).start();

Java 的线程创建和启动非常简单,但如果问一个线程是怎么启动起来的往往并不清楚,甚至不知道为什么启动时是调用start(),而不是调用run()方法呢?

1.Thread.start() ,它是怎么让线程启动的呢? - 图1
以上,就是一个线程启动的整体过程分析,会涉及到如下知识点:

  • 线程的启动会涉及到本地方法(JNI)的调用,也就是那部分 C++ 编写的代码。
  • JVM 的实现中会有不同操作系统对线程的统一处理,比如:Win、Linux、Unix。
  • 线程的启动会涉及到线程的生命周期状态(RUNNABLE),以及唤醒操作,所以最终会有回调操作。也就是调用我们的 run() 方法

接下来,我们就开始逐步分析每一步源码的执行内容,从而了解线程启动过程。

二、线程启动过程

1. Thread start UML 图

1.Thread.start() ,它是怎么让线程启动的呢? - 图2
如图 19-2 是线程的启动过程时序图,整体的链路较长,会涉及到 JVM 的操作。核心源码如下:

  1. Thread.c:https://github.com/unofficial-openjdk/openjdk/blob/jdk/jdk/src/java.base/share/native/libjava/Thread.c(opens new window)
  2. jvm.cpp:https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/prims/jvm.cpp(opens new window)
  3. thread.cpp:https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/thread.cpp(opens new window)
  4. os.cpp:https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/os.hpp(opens new window)
  5. os_linux.cpp:https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/os/linux/vm/os_linux.cpp(opens new window)
  6. os_windows.cpp:https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/os/windows/vm/os_windows.cpp(opens new window)
  7. vmSymbols.hpp:https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/classfile/vmSymbols.hpp(opens new window)

    2. Java 层面 Thread 启动

    2.1 start() 方法

    ```java new Thread(() -> { // todo }).start();

// JDK 源码 public synchronized void start() {

  1. if (threadStatus != 0)
  2. throw new IllegalThreadStateException();
  3. group.add(this);
  4. boolean started = false;
  5. try {
  6. start0();
  7. started = true;
  8. } finally {
  9. try {
  10. if (!started) {
  11. group.threadStartFailed(this);
  12. }
  13. } catch (Throwable ignore) {}
  14. }

}

  1. - 线程启动方法 start(),在它的方法英文注释中已经把核心内容描述出来。Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. 这段话的意思是:由 JVM 调用此线程的 run 方法,使线程开始执行。_其实这就是一个 JVM 的回调过程,下文源码分析中会讲到_
  2. - 另外 start() 是一个 synchronized 方法,但为了避免多次调用,在方法中会由线程状态判断。threadStatus != 0
  3. - group.add(this),是把当前线程加入到线程组,ThreadGroup
  4. - start0(),是一个本地方法,通过 JNI 方式调用执行。这一步的操作才是启动线程的核心步骤。
  5. <a name="bB6VQ"></a>
  6. #### 2.2 start0() 本地方法
  7. ```java
  8. // 本地方法 start0
  9. private native void start0();
  10. // 注册本地方法
  11. public class Thread implements Runnable {
  12. /* Make sure registerNatives is the first thing <clinit> does. */
  13. private static native void registerNatives();
  14. static {
  15. registerNatives();
  16. }
  17. // ...
  18. }
  • start0(),是一个本地方法,用于启动线程。
  • registerNatives(),这个方法是用于注册线程执行过程中需要的一些本地方法,比如:start0、isAlive、yield、sleep、interrupt0等。

registerNatives,本地方法定义在 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. {"interrupt0", "()V", (void *)&JVM_Interrupt},
  12. {"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
  13. {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
  14. {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
  15. {"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
  16. };

JVM_END

  1. - 这部分代码比较多,但核心内容主要是创建线程和启动线程,另外 &thread_entry 也是一个方法,如下:
  2. **thread_entry,线程入口**
  3. ```java
  4. static void thread_entry(JavaThread* thread, TRAPS) {
  5. HandleMark hm(THREAD);
  6. Handle obj(THREAD, thread->threadObj());
  7. JavaValue result(T_VOID);
  8. JavaCalls::call_virtual(&result,
  9. obj,
  10. KlassHandle(THREAD, SystemDictionary::Thread_klass()),
  11. vmSymbols::run_method_name(),
  12. vmSymbols::void_method_signature(),
  13. THREAD);
  14. }

重点,在创建线程引入这个线程入口的方法时,thread_entry 中包括了 Java 的回调函数 JavaCalls::call_virtual。这个回调函数会由 JVM 调用。
vmSymbols::run_method_name(),就是那个被回调的方法,源码如下:
源码https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/classfile/vmSymbols.hpp(opens new window)

  1. #define VM_SYMBOLS_DO(template, do_alias)
  2. template(run_method_name, "run")
  • 这个 run 就是我们的 Java 程序中会被调用的 run 方法。接下来我们继续按照代码执行链路,寻找到这个被回调的方法在什么时候调用的。

    3.2 JavaThread

    ```java native_thread = new JavaThread(&thread_entry, sz);
  1. 接下来,我们继续看 JavaThread 的源码执行内容。<br />**源码**:[https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/thread.cpp(opens new window)](https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/thread.cpp)
  2. ```java
  3. JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  4. Thread()
  5. #if INCLUDE_ALL_GCS
  6. , _satb_mark_queue(&_satb_mark_queue_set),
  7. _dirty_card_queue(&_dirty_card_queue_set)
  8. #endif // INCLUDE_ALL_GCS
  9. {
  10. if (TraceThreadEvents) {
  11. tty->print_cr("creating thread %p", this);
  12. }
  13. initialize();
  14. _jni_attach_state = _not_attaching_via_jni;
  15. set_entry_point(entry_point);
  16. // Create the native thread itself.
  17. // %note runtime_23
  18. os::ThreadType thr_type = os::java_thread;
  19. thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :os::java_thread;
  20. os::create_thread(this, thr_type, stack_sz);
  21. }

众所周知,JVM 是个啥!,所以它的 OS 服务实现,Liunx 还有 Windows 等,都会实现线程的创建逻辑。这有点像适配器模式
os_linux -> os::create_thread

  1. bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
  2. assert(thread->osthread() == NULL, "caller responsible");
  3. // Allocate the OSThread object
  4. OSThread* osthread = new OSThread(NULL, NULL);
  5. // Initial state is ALLOCATED but not INITIALIZED
  6. osthread->set_state(ALLOCATED);
  7. pthread_t tid;
  8. int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
  9. return true;
  10. }
  • osthread->set_state(ALLOCATED),初始化已分配的状态,但此时并没有初始化。
  • pthread_create,是类Unix操作系统(Unix、Linux、Mac OS X等)的创建线程的函数。
  • java_start,重点关注类,是实际创建线程的方法。

    3.4 java_start

    源码https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/os/linux/vm/os_linux.cpp(opens new window) ```java static void java_start(Thread thread) {

    // 线程ID int pid = os::current_process_id();

    // 设置线程 ThreadLocalStorage::set_thread(thread);

    // 设置线程状态:INITIALIZED 初始化完成 osthread->set_state(INITIALIZED);

    // 唤醒所有线程 sync->notify_all();

    // 循环,初始化状态,则一致等待 wait while (osthread->get_state() == INITIALIZED) { sync->wait(Mutex::_no_safepoint_check_flag); }

    // 等待唤醒后,执行 run 方法 thread->run();

    return 0; }

  1. - JVM 设置线程状态,INITIALIZED 初始化完成。
  2. - sync->notify_all(),唤醒所有线程。
  3. - osthread->get_state() == INITIALIZEDwhile 循环等待
  4. - thread->run(),是等待线程唤醒后,也就是状态变更后,才能执行到。_这在我们的线程执行UML图中,也有所体现_
  5. <a name="Fv4wT"></a>
  6. ### 4. JVM 启动线程
  7. ```java
  8. JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  9. JVMWrapper("JVM_StartThread");
  10. JavaThread *native_thread = NULL;
  11. // 创建线程
  12. native_thread = new JavaThread(&thread_entry, sz);
  13. // 启动线程
  14. Thread::start(native_thread);
  15. JVM_END
  • JVM_StartThread 中有两步,创建(new JavaThread)、启动(Thread::start)。创建的过程聊完了,接下来我们聊启动。

    4.1 Thread::start

    源码https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/thread.cpp(opens new window) ```java void Thread::start(Thread* thread) { trace(“start”, thread);

    if (!DisableStartThread) { if (thread->is_Java_thread()) {

    1. java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
    2. java_lang_Thread::RUNNABLE);

    } // 不同的 OS 会有不同的启动代码逻辑 os::start_thread(thread); } }

  1. - 如果没有禁用线程 DisableStartThread 并且是 Java 线程 thread->is_Java_thread(),那么设置线程状态为 RUNNABLE
  2. - os::start_thread(thread),调用线程启动方法。_不同的 OS 会有不同的启动代码逻辑_
  3. <a name="seAlP"></a>
  4. #### 4.2 os::start_thread(thread)
  5. **源码**:[https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/os.hpp(opens new window)](https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/os.hpp)
  6. ```java
  7. void os::start_thread(Thread* thread) {
  8. // guard suspend/resume
  9. MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
  10. OSThread* osthread = thread->osthread();
  11. osthread->set_state(RUNNABLE);
  12. pd_start_thread(thread);
  13. }
  • osthread->set_state(RUNNABLE),设置线程状态 RUNNABLE
  • pdstart_thread(thread),启动线程,这个就由各个 OS 实现类,实现各自系统的启动方法了。比如,windows系统和Linux系统的代码是完全不同的。_

    4.3 pd_start_thread(thread)

    源码https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/os/linux/vm/os_linux.cpp(opens new window) ```java 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(); }
  1. - 这部分代码 notify() 最关键,它可以唤醒线程。
  2. - 线程唤醒后,3.4 中的 thread->run(); 就可以继续执行了。
  3. <a name="PSwpj"></a>
  4. ### 5. JVM 线程回调
  5. <a name="cBAoQ"></a>
  6. #### 5.1 thread->run()[JavaThread::run()]
  7. **源码**:[https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/thread.cpp(opens new window)](https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/thread.cpp)
  8. ```java
  9. // The first routine called by a new Java thread
  10. void JavaThread::run() {
  11. // ... 初始化线程操作
  12. thread_main_inner();
  13. }
  • os_linux.cpp 类中的 java_start 里的 thread->run(),最终调用的就是 thread.cpp 的 JavaThread::run() 方法。
  • 这部分还需要继续往下看,thread_main_inner(); 方法。

    5.2 thread_main_inner

    源码https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/thread.cpp(opens new window) ```java void JavaThread::thread_main_inner() {

    if (!this->has_pending_exception() &&

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

    {

    1. ResourceMark rm(this);
    2. this->set_native_thread_name(this->get_thread_name());

    } HandleMark hm(this); this->entry_point()(this, this); }

    DTRACE_THREAD_PROBE(stop, this);

    this->exit(false); delete this; }

```

  • 这里有你熟悉的设置的线程名称,this->set_native_thread_name(this->get_thread_name())。
  • this->entry_point(),实际调用的就是 3.1 中的 thread_entry 方法。
  • thread_entry,方法最终会调用到 JavaCalls::call_virtual 里的vmSymbols::run_method_name()。也就是 run() 方法,至此线程启动完成。