The underlying analysis of the thread created by the start() method of the Thread class

In Java, a new thread can be created by the following simple code

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

In start(), start0() is called again, which is the real method of creating threads.

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() is a native method, which means that it is specifically implemented by C/C++.

Due to the open source agreement of Oracle jdk, the local method code file implemented by c/c++ is compressed into a dll library file, we cannot directly view it, choose to download openjdk to view it. Official website address: OpenJDK

 You can choose Mercurial or Github to download, choose Github here

There are many versions provided, here choose jdk8

Click to download.

After the source code is downloaded, how can I view it? For Thread.java, there is a corresponding Thread.c file in the jdk source code

 Going back to the Thread class, there is the following piece of code at the beginning

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

Its function is to call registerNatives() first when creating an instance of the Thread class, that is, to call Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) in Thread.c. This method internally calls RegisterNatives to associate the method in the Thread class with the JVM method, and the JVM method corresponding to start0 is JVM_StartThread.

 The JVM_StartThread method is located in jvm.cpp in the hotspot virtual machine and is written in C++ language.

 In this method, a JavaThread object will be created

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

Call the thread creation method of the operating system in the JavaThread construction method

 Support multiple systems as follows

 Choose linux here, and eventually the system function pthread_create will be called to create a thread. This step switches from user mode to kernel mode, and then switches from kernel mode to user mode after the thread is successfully created. In Java, it is not recommended to create new threads frequently, but to use the thread pool to achieve thread reuse, because switching from user mode to kernel mode will have a certain overhead.

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

In Linux, you can view its usage through the command man pthread_create

Note: If No manual entry for pthread_create is prompted in CentOS, you need to install the package: yum install man-pages libstdc++-docs

The third parameter of this function indicates the method executed after the thread is created, here is java_start. The fourth parameter represents the parameter passed to the java_start method. From the context, here is the JavaThread object created earlier.

Then look at the java_start method, still in os_linux.cpp, the key code is as follows

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;
}

In the method, the kernel thread state is set to the initial state through osthread->set_state(INITIALIZED), and then the run method of the JavaThread object will be called in the while loop until the state is not equal to INITIALIZED. When will the kernel thread state change? This goes back to the JVM_StartThread method in jvm.cpp, which will call the following method at the end

Thread::start(native_thread);

This method will call os::start_thread(thread)

 os::start_thread(thread) first finds the corresponding kernel thread through the JavaThread object, and then sets the kernel thread state to RUNNABLE.

 Going back to the above picture again, the while loop condition is not satisfied, and finally the following thread->run() can be executed

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;
}

 Corresponding to the method in thread.cpp, the key code is as follows

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

The thread_main_inner method is called internally, the key code 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;
}

The value returned by entry_point() is _entry_point, which is assigned when creating a JavaThread object and is a pointer to the thread_entry method. In this method, JavaCalls::call_virtual will be called, and it can be seen from the parameter run_method_name that the run method in the Thread class is finally called.

Summary: When the start method of the Thread object is called, it calls the start0 method internally. Then call the method written in C++ in the virtual machine through JNI technology, in which a JavaThread object will be created, and the system function pthread_create will be called in its construction method to create a kernel thread. Finally, the run method of the Thread object is executed in the kernel thread.

Guess you like

Origin blog.csdn.net/u010389391/article/details/128150439