Java线程启动详解——Thread是如何执行run()方法的?

内容纲要

引入

在Java中,线程是实现并发编程的重要工具。为了让初学者和不熟悉底层实现的工程师也能理解线程的启动过程,我们将详细解析从Java代码调用到JVM虚拟机内部和操作系统级别的线程创建与启动过程。本文将结合具体的代码和JVM实现细节,解释线程是如何从调用start()方法开始到最终运行run()方法的全过程。

一、Java代码启动线程

首先,我们从Java代码开始。在Java中,我们通常通过以下方式启动一个线程:

public class ThreadDemo {
    public static void main(String[] args) {
        Thread t = new Thread(() -> System.out.println("Thread is running"));
        t.start();  // 启动线程
    }
}

在这个例子中,我们创建了一个线程对象t,并调用t.start()方法启动线程。

二、JDK源码分析

1. Thread.java

当我们调用start()方法时,JDK源码中的Thread.java类会执行以下逻辑:

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    group.add(this);
    boolean started = false;
    try {
        start0();  // 调用本地方法
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
        }
    }
}

start()方法调用了一个本地方法start0(),这是一个使用JNI(Java Native Interface)实现的本地方法。

2. Thread.c

Thread.c文件中,start0()方法被注册为JVM中的一个本地方法:

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    // 其他方法
};

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) {
    (*env)->RegisterNatives(env, cls, methods, sizeof(methods) / sizeof(methods[0]));
}

通过RegisterNatives函数,start0()方法被映射为JVM_StartThread方法。

三、JVM虚拟机源代码分析

1. jvm.cpp

在JVM实现中,JVM_StartThread方法负责启动一个新的线程:

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) {
    // JVM内部实现细节
    // 1. 加锁线程
    // 2. JVM创建线程
    // 3. 将线程挂起,等待操作系统线程创建
    // 4. 启动JVM线程
} JVM_END


JVM_StartThread方法进行了一系列的操作,包括加锁、创建线程等。

2. thread.cpp

thread.cpp中,JavaThread::JavaThread()构造函数用于创建一个新的Java线程对象:

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) {
    // 创建新的Java线程对象
    os::create_thread(this, stack_sz);
}

os::create_thread()方法用于调用操作系统的线程创建函数。

3. os.cpp

os.cpp中,os::create_thread()方法实现了对操作系统线程创建函数的调用(真正的方法实现在os_linux.cpp中):

bool os::create_thread(Thread* thread, size_t stack_size) {
    // 调用操作系统的pthread_create函数创建线程
    return pthread_create(...);
}

4. os_linux.cpp

最终,pthread_create函数在操作系统级别创建线程,并调用java_start()方法启动线程:

void* java_start(Thread* thread) {
    // 执行Thread的run()方法
    thread->run();
}


...

至此,Java线程的run()方法开始执行。

四、总结

整个线程启动过程如下:

  1. Java代码层面:调用Thread对象的start()方法。
  2. JDK层面start()方法调用本地方法start0()start0()通过JNI调用JVM中的JVM_StartThread方法。
  3. JVM层面JVM_StartThread方法创建并启动新的Java线程,调用os::create_thread()方法创建系统级线程。
  4. 操作系统层面pthread_create函数创建操作系统线程,并调用java_start()方法执行线程的run()方法。

流程小结:
① 线程类被JVM加载时会绑定native方法与对应的C++方法
② start()方法执行:
start()➔native start0()➔JVM_Thread➔ 创建线程JavaThread::JavaThread
③ 创建OS线程,指定OS线程运行入口:
创建线程构造方法➔ 创建OS线程➔指定OS线程执行入口,就是线程的run()方法
④ 启动OS线程,运行时会调用指定的运行入口run()方法。至此,实现一个的线程运行
⑤ 创建线程的过程是线程安全的,基于操作系统互斥量(MutexLocker)保证互斥,所以说创建线程性能很差

通过上述步骤,Java线程从Java代码调用到JVM虚拟机内部和操作系统级别的线程创建与启动的全过程被完整地展现出来。

参考资料

下载jdk源代码的方法

git clone https://github.com/openjdk/jdk8u

希望这篇文章能帮助你更好地理解Java线程的启动过程。如果你有任何问题或建议,欢迎在评论区留言讨论。

Leave a Comment

您的电子邮箱地址不会被公开。 必填项已用*标注

close
arrow_upward