How is the run() method called after thread.start ()?

preface

When we first learned about threads in Java, we probably wrote code like this:

public static void main(String[] args) {
        Thread thread = new Thread(() ->
                System.out.println("Current thread name:" + Thread.currentThread().getName())
                , "jts-thead-1");
        thread.start();
    }
Copy the code

Is equivalent to:

public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println(" Thread.currentThread().getName() "); } }, "jts-thead-1"); thread.start(); }Copy the code

The print result is as follows:

Current thread name: jts-thead-1
Copy the code

Did you ever wonder why I printed this statement when I called thread.start ()? You can think about it for a while, but it’s time to explore.

Java layer code

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) { } } }Copy the code
private native void start0();
Copy the code

It can be found that this is a native method. If we want to study the native method, we need to track it into hotspot source code.

2. ExplorehotspotThe source code

2.1 Environment Preparation

Here I have downloaded the source code of OpenJDK8, using the tool clion, which is jetBrains software, and the use is the same as idea. Fun note: Building the openJDK source code was a difficult process, and I encountered many unexpected potholes during the process, mostly due to environmental issues. OpenJDK with CLion, you just need to install the Docker on your own machine, and then follow the above tutorial. Note that the source code is based on JDK15, if you want to use a different version, in the script to replace the address can be I use JDK8, I looked at the JDK8 in different small versions also some differences, but generally the code is the same.

2.2 Search Rules

The source code for Hotspot is written in C and C ++. As we saw above, when Java starts a thread, the start0() method is finally called. How does the start0() method map to Hotspot code?

If we don’t knowJavaIn thenativeMethods in theHotspotThe corresponding rules can be found inHotspotSource code inside the global searchstart0(), the results are as follows:This is inThread.cIn the file. And you can see it maintains an array in there to mapJavaIn thenativeMethods andHotspotMethods. You can see it here as wellThread.javaCorresponds to thisThread.cYou can also compare other documents to see if there is such a pattern. Since the method has a lot of content, I will cut out the secondary code in the following code and keep only the main process code so that it is clearer.

Since we’re looking at the start0() method, we’ll just click on the JVM_StartThread() method and start exploring it for real.

2.3 A journey of discovery

The JVM_StartThread method is in the jvm. CPP file:

JVM_ENTRY(void.JVM_StartThread(JNIEnv* env, jobject jthread))
  JavaThread *native_thread = NULL;
  {
      // Create native threads note: &thread_entry is a method.
      native_thread = new JavaThread(&thread_entry, sz);
      if (native_thread->osthread() != NULL) {
        // Do some preparatory work for the thread
        native_thread->prepare(jthread);
      }
    }
  }
  Thread::start(native_thread);
Copy the code

This calls the JavaThread constructor, which passes a thread_entry parameter that is actually a method. We can click on it to see:

static void thread_entry(JavaThread* thread, TRAPS) {
  HandleMark hm(THREAD);
  Handle obj(THREAD, thread->threadObj());
  JavaValue result(T_VOID);
  JavaCalls::call_virtual(&result,
                          obj,
                          vmClasses::Thread_klass(),
                          vmSymbols::run_method_name(),
                          vmSymbols::void_method_signature(),
                          THREAD);
}
Copy the code

We need to pay attention to a few parameters in the call_virtual method:

  • obj: is the current thread object
  • vmClasses::Thread_klass(): indicates that the type of the invoked object isjava.lang.Thread
  • vmSymbols::run_method_name(): indicates that the invoked method isrun
  • vmSymbols::void_method_signature(): Indicates that the returned result is null.

The mapping is as follows: DO_klass (Thread_klass,java_lang_Thread) in VMClassMacros. HPP

VmSymbols. The template in the HPP (run_method_name, “run”)Underline: Please remember this method of registration, the exam will be tested.

Then trace to the JavaThread() constructor: JavaThread()

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : JavaThread() {
  // Set the callback function, which was highlighted earlier
  set_entry_point(entry_point);
  // Create a thread
  os::create_thread(this, thr_type, stack_sz);
}
Copy the code

This registers the callback function with the thread.

Next we look at the method create_thread() that creates the thread. This method is defined in os.hpp, and since Java is cross-platform, it can be implemented differently on different operating systems. We can consider os.hpp as an interface, and os_linux. CPP, OS_Windows. CPP and other operating system files are its implementation classes. I’m mainly looking at the code corresponding to the Linux platform, and the next code presentation is the code corresponding to the Linux platform.

bool os::create_thread(Thread* thread, ThreadType thr_type,
                       size_t req_stack_size) {
  ret = pthread_create(&tid, &attr, (void* (*) (void*)) thread_native_entry, thread);
}
Copy the code

Again, you can see that thread_native_entry is called,

static void *thread_native_entry(Thread *thread) {{// notify parent thread
    osthread->set_state(INITIALIZED);
    sync->notify_all(a);// wait until os::start_thread() 
    while (osthread->get_state() == INITIALIZED) {
      sync->wait_without_safepoint_check();
    }
  }
  / / callback run
  thread->call_run(a);return 0;
}
Copy the code

When will the current thread be waked up? Until the start_thread() method is called. What do you do when you wake up? The callback to run() is the callback to the method we just registered with the thread. CPP calls call_run() to notify threads. CPP calls call_run() to notify threads.

void Thread::call_run(a) {
  // Invoke <ChildClass>::run()
  this->run(a); }Copy the code

Continue tracing the run() method:

void JavaThread::run(a) {
  // 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(a); }Copy the code

Continue tracing thread_main_inner()

void JavaThread::thread_main_inner(a) {
    this->entry_point() (this.this);
}
Copy the code

This will call the entry_point() method that we just set up when set_entry_point(entry_point)! This means that the java.lang.thread.run () method is called here! Let’s look at where notify is made after the thread is created and wait is made. OS ::start_thread() notify OS ::start_thread() We continue with the Thread::start(native_thread) method above, which is in thread.cpp:

void Thread::start(Thread* thread) {
  if (thread->is_Java_thread()) {
    java_lang_Thread::set_thread_status(JavaThread::cast(thread)->threadObj(),
                                        JavaThreadStatus::RUNNABLE);
  }
  os::start_thread(thread);
}
Copy the code

Click to OS: : start_thread (thread); Method to continue tracing, which is done in os.cpp:

void os::start_thread(Thread* thread) {
  OSThread* osthread = thread->osthread(a); osthread->set_state(RUNNABLE);
  pd_start_thread(thread);
}
Copy the code

Os_linux. CPP pd_start_thread(thread) :

void os::pd_start_thread(Thread* thread) {
  sync_with_child->notify(a); }Copy the code

Annotation really does not cheat me. Notify OS ::start_thread(thread)

3. Summary

After thread.start () starts the Thread, thread.run () is called back by the JVM. The answer is already stated in the Thread. Java class comment:

Lol more boring knowledge. However, through this knowledge point, and reviewed the university to learnCandC++, and then also understand some details of the start of some threads, but also take a look at the thread lifecycle related code, or very fruitful.

We can see that thread.run () returns void. What would you do if we wanted to get the result return value and let you implement it yourself?

Well, that’s all for this article, thank you for watching! Welcome to pay attention to my public account, “The Technical Guide to Javah”.