The basic concept

Java threads are actually mapped to the kernel threads of the operating system, so Java threads are basically managed by the operating system. In Linux, threads and processes are described using the same structure, but the process has its own address space, and multiple threads of the same process share resources.

This article is based on OpenJDK 1.8

Thread state

The switching conditions of each thread state and the calling method are shown below:Java Thread states are defined in the thread. State enumeration as follows

public enum State {
    // It is newly created and not started
    NEW,

    // It is running in the JVM and may also be waiting for other resources from the operating system
    RUNNABLE,

    // blocked and waiting for a monitor lock
    BLOCKED,

    // A thread in the wait state is waiting for another thread to perform a specific operation
    WAITING,

    // The maximum waiting time can be set
    TIMED_WAITING,

    / / end
    TERMINATED;
}
Copy the code

Thread creation

  1. Inheriting the Thread class, the code is as follows:
class PrimeThread extends Thread {
    long minPrime;
    PrimeThread(long minPrime) {
        this.minPrime = minPrime;
    }

    public void run(a) {
        // compute primes larger than minPrime..}}// Start the thread
PrimeThread p = new PrimeThread(143);
p.start();
Copy the code
  1. Implement the Runable interface with the following code (usually recommended):
class PrimeRun implements Runnable {
    long minPrime;
    PrimeRun(long minPrime) {
        this.minPrime = minPrime;
    }

    public void run(a) {
        // compute primes larger than minPrime..}}// Start the thread
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
Copy the code

The hotspot source

JNI mechanism

JNI is short for Java Native Interface, which provides several apis to implement communication between Java and other languages (mainly C and C++). Application scenarios of JNIWhen you have older libraries that are already written in C, porting to Java would be a waste of time, whereas JNI allows Java programs to interact with libraries written in C, making porting unnecessary. You can use JNI to interact with hardware, the operating system, improve application performance, and so on. One thing to note is that you need to ensure that native code works in any Java virtual machine environment.

Once JNI is used, Java programs lose two advantages of the Java platform:

  1. The application is no longer cross-platform. To be cross-platform, the native language part of the application must be compiled and configured in different system environments.
  2. Programs are no longer perfectly safe, and improper use of native code can cause the entire program to crash. A general rule is that calling native methods should be concentrated in a small number of classes, which reduces the coupling between Java and other languages.

For example, this operation is more, you can refer to the following materials

www.runoob.com/w3cnote/jni…

Start the process

The startup process is as follows

Thread start

Java creates a Thread instance by starting the Thread with the start method. Inside the start method, the local method start0() is called. We can use this method as an entry point to analyze the JVM’s underlying implementation of Thread.

public synchronized void start(a) {
    // Check thread status
    if(threadStatus ! =0)
        throw new IllegalThreadStateException();

    // Add to the group
    group.add(this);

    boolean started = false;
    try {
        // Start the thread
        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(a);
Copy the code

Start0 () is a native method and we can look up the java_lang_Thread_start0 function in the hotspot virtual source code according to the JNI specification. The definition is as follows:

/* * Class: java_lang_Thread * Method: start0 * Signature: ()V */
JNIEXPORT void JNICALL Java_java_lang_Thread_start0
  (JNIEnv *, jobject);
Copy the code

I could guess from the Method: start0 comment that the start0 Method might also exist inside the JVM, so I searched for it again and found the thread.c file. You can see that there is a Java_java_lang_Thread_registerNatives() method, which is used to initialize the bindings between Thread.java and other methods. This method is called in the first static block of threa.java, ensuring that it is the first method to be called in class loading. The purpose of this native method is to register other native methods into the JVM. The code looks like this:

static JNINativeMethod methods[] = {
    {"start0"."()V",        (void *)&JVM_StartThread},
    {"stop0"."(" OBJ ")V", (void *)&JVM_StopThread},
    {"isAlive"."()Z",        (void *)&JVM_IsThreadAlive},
    {"suspend0"."()V",        (void *)&JVM_SuspendThread},
    {"resume0"."()V",        (void *)&JVM_ResumeThread},
    {"setPriority0"."(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield"."()V",        (void *)&JVM_Yield},
    {"sleep"."(J)V",       (void *)&JVM_Sleep},
    {"currentThread"."()" THD,     (void *)&JVM_CurrentThread},
    {"countStackFrames"."()I",        (void *)&JVM_CountStackFrames},
    {"interrupt0"."()V",        (void *)&JVM_Interrupt},
    {"isInterrupted"."(Z)Z",       (void *)&JVM_IsInterrupted},
    {"holdsLock"."(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {"getThreads"."()" THD,   (void *)&JVM_GetAllThreads},
    {"dumpThreads"."([" THD "The [[" STE, (void *)&JVM_DumpThreads},
    {"setNativeName"."(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

#undef THD
#undef OBJ
#undef STE
#undef STR

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
Copy the code

Back to our start0 method, at this time we will go to find JVM_StartThread method is in he is in the hotspot/SRC/share/vm/prims/JVM. CPP this file:

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

  // We cannot hold the Threads_lock when we throw an exception,
  // due to rank ordering issues. Example: we might need to grab the
  // Heap_lock while we construct the exception.
  bool throw_illegal_thread_state = false;

  // We must release the Threads_lock before we can post a jvmti event
  // in Thread::start.
  {
    // Ensure that the C++ Thread and OSThread structures aren't freed before
    // we operate.
    MutexLocker mu(Threads_lock);

    // 1. Check whether the Java thread is started. If so, throw an exception
    if(java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) ! = NULL) { throw_illegal_thread_state =true;
    } else {
      // 2. If not, the thread will be created
      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));  
      size_t sz = size > 0 ? (size_t) size : 0;
      // The virtual machine creates a JavaThread, which creates operating system threads internally and then associates them with Java threads
      native_thread = new JavaThread(&thread_entry, sz);

      if(native_thread->osthread() ! = NULL) {// Note: the current thread is not being used within "prepare".native_thread->prepare(jthread); }}}if (throw_illegal_thread_state) {
    THROW(vmSymbols::java_lang_IllegalThreadStateException());
  }

  assert(native_thread ! = NULL,"Starting null thread?");

  if (native_thread->osthread() == NULL) {
    // No one should hold a reference to the 'native_thread'.
    delete native_thread;
    if (JvmtiExport::should_post_resource_exhausted()) {
      JvmtiExport::post_resource_exhausted(
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
        "unable to create new native thread");
    }
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }

  // Set the thread state to Runnable
  Thread::start(native_thread);

JVM_END
Copy the code

The JavaThread constructor uses OS ::create_thread to create the corresponding Java kernel thread

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  Thread()
{
  if (TraceThreadEvents) {
    tty->print_cr("creating thread %p".this);
  }
  initialize();
  _jni_attach_state = _not_attaching_via_jni;
  set_entry_point(entry_point);
  os::ThreadType thr_type = os::java_thread;
  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
                                                     os::java_thread;
  // Create the kernel line corresponding to the Java thread
  os::create_thread(this, thr_type, stack_sz);
  _safepoint_visible = false;
}

Copy the code

OS: create_thread is actually a to support cross-platform create threads, in Linux, for example, the hotspot/SRC/Linux OS / / vm/os_linux CPP) :

bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
  // ...
  
  // Create an OSThread kernel thread object
  OSThread* osthread = new OSThread(NULL, NULL);
  / / binding
  thread->set_osthread(osthread);

  pthread_t tid;
  // pthread_create is a Linux API used to create threads.
  int ret = pthread_create(&tid, &attr, (void* (*) (void*)) java_start, thread);
  // ...  
  return true;
}
Copy the code

Interface information can be queried through the ubantu console

Man pthread_create to query documents

We can see from the documentation that whenpthread_createAfter the thread is created, the function calls the callback passed with the third argument

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

In this case, the javA_start function

// Thread start routine for all newly created threads
static void *java_start(Thread *thread) {

  // Call Thread's run method
  thread->run();

  return 0;
}
Copy the code

In thread. CPP, the JavaThread::run method ends up calling thread_main_inner:

// The first routine called by a new Java thread
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();

  // Note, thread is no longer valid at this point!
}
Copy the code

Within the thread_main_inner method, call the entry_point method passed in when we created the JavaThread object earlier:

void JavaThread::thread_main_inner(a) {

  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);
    Call the entry_point method
    this->entry_point()(this.this);
  }

  DTRACE_THREAD_PROBE(stop, this);

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

Copy the code

From the above code we can see that we create a JavaThread object and pass in the thread_entry method

// JVM_StartThread creates an operating system thread and executes thread_entry
static void thread_entry(JavaThread* thread, TRAPS) {
  HandleMark hm(THREAD);
  Handle obj(THREAD, thread->threadObj());
  JavaValue result(T_VOID);
  // thrad.start () calls the run method of the java.lang.thread class
  JavaCalls::call_virtual(&result,
                          obj,
                          KlassHandle(THREAD, SystemDictionary::Thread_klass()),
                          vmSymbols::run_method_name(),
                          vmSymbols::void_method_signature(),
                          THREAD);
}
Copy the code

Let’s look again at the Run method of our Java Thread class

public void run(a) {
    if(target ! =null) {
        // thread.run () calls runable.run ()target.run(); }}Copy the code

The resources

  • www.jb51.net/article/216…
  • Blog.csdn.net/u013928208/…
  • www.cnblogs.com/whhjava/p/9…
  • www.runoob.com/w3cnote/jni…
  • Developer.51cto.com/art/202011/…
  • Blog.csdn.net/weixin_3438…
  • Blog.csdn.net/weixin_3026…
  • zhuanlan.zhihu.com/p/33830504