• Thread类的start()方法创建线程的底层分析


    在Java中通过如下简单代码就可以创建一个新线程

    1. Thread thread = new Thread(new Runnable() {
    2. @Override
    3. public void run() {
    4. //do something
    5. }
    6. });
    7. thread.start();

    在start()中又调用了start0(),它才是真正创建线程的方法。

    1. public synchronized void start() {
    2. group.add(this);
    3. boolean started = false;
    4. try {
    5. start0();
    6. started = true;
    7. } finally {
    8. try {
    9. if (!started) {
    10. group.threadStartFailed(this);
    11. }
    12. } catch (Throwable ignore) {
    13. /* do nothing. If start0 threw a Throwable then
    14. it will be passed up the call stack */
    15. }
    16. }
    17. }
    18. private native void start0();

    start0()是一个native方法,这就表明它具体是由C/C++来实现。

    由于Oracle jdk的开源协议问题,由c/c++实现的本地方法代码文件被压解成dll库文件,我们不能直接查看,选择下载 openjdk查看。官网地址:OpenJDK

     可以选择Mercurial或Github进行下载,此处选择Github

    里面提供了很多版本,此处选择jdk8

    点击下载即可。

    源码下载后,该如何查看呢?针对Thread.java,jdk源码中有一个对应的Thread.c文件

     再回到Thread类,在开头有如下一段代码

    1. private static native void registerNatives();
    2. static {
    3. registerNatives();
    4. }

    它的作用是在创建Thread类的实例时会先调用registerNatives(),也就是调用Thread.c中的Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)。该方法内部又调用RegisterNatives,将Thread类中的方法与JVM方法进行关联,start0对应的JVM方法就是JVM_StartThread。

     JVM_StartThread方法位于hotspot虚拟机里的jvm.cpp,由C++语言编写。

     在该方法里会创建JavaThread对象

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

    在JavaThread构造方法里调用操作系统的创建线程方法

     支持如下多个系统

     此处选择linux,最终会调用系统函数pthread_create创建线程。这一步就从用户态切换到了内核态,线程创建成功后再从内核态切换到用户态。在Java中不建议频繁创建新线程而是使用线程池做到线程复用,就是因为用户态到内核态的切换会有一定的开销。

    int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

    在Linux中通过命令man pthread_create可查看其用法

    注:在CentOS中如果提示No manual entry for pthread_create,需要安装包:yum install man-pages libstdc++-docs

    该函数的第三个参数表示在线程创建后执行的方法,此处是java_start。第四个参数表示传入java_start方法的参数,从上下文来看,此处是前面创建的JavaThread对象。

    接着看java_start方法,还是在os_linux.cpp中,关键代码如下

    1. static void *java_start(Thread *thread) {
    2. {
    3. MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
    4. // notify parent thread
    5. osthread->set_state(INITIALIZED);
    6. sync->notify_all();
    7. // wait until os::start_thread()
    8. while (osthread->get_state() == INITIALIZED) {
    9. sync->wait(Mutex::_no_safepoint_check_flag);
    10. }
    11. }
    12. // call one more level start routine
    13. thread->run();
    14. return 0;
    15. }

    方法里通过osthread->set_state(INITIALIZED)将内核线程状态置为初始状态,接下来while循环直到状态不等于INITIALIZED就会调用JavaThread对象的run方法。那内核线程状态什么时候会改变呢?这又要回到jvm.cpp中的JVM_StartThread方法,在该方法最后会调用如下方法

    Thread::start(native_thread);

    该方法会调用os::start_thread(thread)

     os::start_thread(thread)内部先通过JavaThread对象找到对应的内核线程,然后将内核线程状态置为RUNNABLE。

     再次回到上面这张图,while循环条件不满足,终于可以执行下面的thread->run()

    1. static void *java_start(Thread *thread) {
    2. {
    3. MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
    4. // notify parent thread
    5. osthread->set_state(INITIALIZED);
    6. sync->notify_all();
    7. // wait until os::start_thread()
    8. while (osthread->get_state() == INITIALIZED) {
    9. sync->wait(Mutex::_no_safepoint_check_flag);
    10. }
    11. }
    12. // call one more level start routine
    13. thread->run();
    14. return 0;
    15. }

     对应thread.cpp里的方法,关键代码如下

    1. void JavaThread::run() {
    2. // We call another function to do the rest so we are sure that the stack addresses used
    3. // from there will be lower than the stack base just computed
    4. thread_main_inner();
    5. }

    内部又调用thread_main_inner方法,关键代码this->entry_point()(this, this)

    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. this->entry_point()(this, this);
    15. }
    16. DTRACE_THREAD_PROBE(stop, this);
    17. this->exit(false);
    18. delete this;
    19. }

    entry_point()返回的值为_entry_point,该值在创建JavaThread对象时进行赋值,是thread_entry方法的指针。在该方法中会调用JavaCalls::call_virtual,通过参数run_method_name可以看出最终调用的是Thread类中的run方法。

    总结:当调用Thread对象的start方法后,它的内部调用start0方法。接着通过JNI技术调用虚拟机里用C++编写的方法,在该方法中会创建JavaThread对象,在其构造方法中调用系统函数pthread_create创建内核线程。最终在内核线程中执行Thread对象的run方法。

  • 相关阅读:
    Spring框架详解
    NLog自定义Target之MQTT
    子进程变成僵尸进程
    【华为OD机试真题 JS】字符串变换最小字符串
    Spring Boot学习笔记
    华清远见(上海中心)
    计算机视觉 01(介绍)
    了解事件冒泡
    redis集群
    数组c++介绍
  • 原文地址:https://blog.csdn.net/u010389391/article/details/128150439