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

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

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        //do something
    }
});
thread.start();

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

public synchronized void start() {
    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();

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

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

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

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

点击下载即可。

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

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

private static native void registerNatives();
static {
    registerNatives();
}

它的作用是在创建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中,关键代码如下

static void *java_start(Thread *thread) {
  {
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);

    // notify parent thread
    osthread->set_state(INITIALIZED);
    sync->notify_all();

    // wait until 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;
}

方法里通过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()

static void *java_start(Thread *thread) {
  {
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);

    // notify parent thread
    osthread->set_state(INITIALIZED);
    sync->notify_all();

    // wait until 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.cpp里的方法,关键代码如下

void JavaThread::run() {

  // We call another function to do the rest so we are sure that the stack addresses used
  // from there will be lower than the stack base just computed
  thread_main_inner();
}

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

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);
    this->entry_point()(this, this);
  }

  DTRACE_THREAD_PROBE(stop, this);

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

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方法。

猜你喜欢

转载自blog.csdn.net/u010389391/article/details/128150439