Hi~ O ( ̄▽ ̄)ブ ブ, good morning, my friends

Get on the adventurer 4ye boat and explore hotspot source continent ~ 😝

This article introduces the mystery of Thread start

Thread Start source code revealed

public synchronized void start(a) {
    /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */
    if(threadStatus ! =0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        // Look here ~
        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 */}}}Copy the code

From the above code, we can see that threadStatus checks to see if it is 0 and raises an exception if it is not.

ThreadStatus defaults to 0

  • When will that change?

  • What are the values of this threadStatus?

Hey hey with a little doubt to continue to see pa ~ 😝


start0

The change in state must be accompanied by the start of the thread, so we go straight to the start0 method below

private native void start0(a);
Copy the code

It can be found that it is a native function and we can directly

The OpenJDK JDK source code global search, you can find it

Path: the JDK \ SRC \ share \ native \ Java \ lang \ Thread c

JNINativeMethod

This JNINativeMethod is a structure

Path: the JDK \ SRC \ share \ javavm \ export \ jni h

/* * used in RegisterNatives to describe native method name, signature, * and function pointer. */
typedef struct {
    char *name;
    char *signature;
    void *fnPtr;
} JNINativeMethod;
Copy the code

JVM_StartThread

Along the way, we found the JVM_StartThread method

path: jdk\src\share\javavm\export\jvm.h

/* * java.lang.Thread */
JNIEXPORT void JNICALL
JVM_StartThread(JNIEnv *env, jobject thread);
Copy the code

Here JNIEXPORT and JNICALL are JNI keywords, indicating that this function is to be called by JNI

Seeing that it is a JVM starting file, let’s go to the following hotspot source and look at 😋

Found here is the use of 👉

path: hotspot\src\share\vm\prims\jvm.cpp

Code:
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);

    // Since JDK 5 the java.lang.Thread threadStatus is used to prevent
    // re-starting an already started thread, so we should usually find
    // that the JavaThread is null. However for a JNI attached thread
    // there is a small window between the Thread object being created
    // (with its JavaThread set) and the update to its threadStatus, so we
    // have to check for this
    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) ! =NULL) {
      throw_illegal_thread_state = true;
    } else {
      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running

      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      // Allocate the C++ Thread structure and create the native thread. The
      // stack size retrieved from java is signed, but the constructor takes
      // size_t (an unsigned type), so avoid passing negative values which would
      // result in really large stacks.
      size_t sz = size > 0 ? (size_t) size : 0;
      native_thread = new JavaThread(&thread_entry, sz);

      // At this point it may be possible that no osthread was created for the
      // JavaThread due to lack of memory. Check for this situation and throw
      // an exception if necessary. Eventually we may want to change this so
      // that we only grab the lock if the thread was created successfully -
      // then we can also do this check and throw the exception in the
      // JavaThread constructor.
      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");
  }

 // Look here ~
  Thread::start(native_thread);

JVM_END
Copy the code

Here JVM_ENTRY and JVM_END are two macro definitions that define the start and end of the function body. 😄

There are too many details here to chew 😅

Talk about a few ~ cough cough also look at the author’s annotation translation 🐷

Such as:

Create a thread

native_thread = new JavaThread(&thread_entry, sz);
Copy the code
Go to the constructor
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  Thread(a)#if INCLUDE_ALL_GCS
  , _satb_mark_queue(&_satb_mark_queue_set),
  _dirty_card_queue(&_dirty_card_queue_set)
#endif // INCLUDE_ALL_GCS
{
  if (TraceThreadEvents) {
    tty->print_cr("creating thread %p".this);
  }
  initialize(a); _jni_attach_state = _not_attaching_via_jni;set_entry_point(entry_point);
  // Create the native thread itself.
  // %note runtime_23
  os::ThreadType thr_type = os::java_thread;
  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
                                                     os::java_thread;
  // Look here ~
   os::create_thread(this, thr_type, stack_sz);
  _safepoint_visible = false;
 
}
Copy the code
Creating a kernel thread

Found here is to create a kernel thread ~ 👍

os::create_thread(this, thr_type, stack_sz);
Copy the code

Follow the train of thought continue to explore ~

Linux scenario

There is the following code 🐂

path: hotspot\src\os\linux\vm\os_linux.cpp

Finally, a function that looks vaguely familiar

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

The bottom content is really crazy ~ endless.. Ha, ha, ha

Here, cut off the pointer function java_start, if you are interested, keep going! 😄

Starting a thread

Thread::start(native_thread); 支那

Thread::start

Come down here ~

path: hotspot\src\share\vm\runtime\thread.cpp

The function of the first red box:

Before starting the thread, initialize the thread state to RUNNABLE.

Cannot be set after the thread is started because we do not know the correct thread state, which may be in MONITOR_WAIT or in sleep or other states.

The second red box starts the kernel thread!

// The INITIALIZED state is distinguished from the SUSPENDED state because the // conditions in which a thread is first started are different from those in which // a suspension is resumed. These differences make it hard for us to apply the  // tougher checks when starting threads that we want to do when resuming them. // However, when start_thread is called as a result of Thread.start, on a Java // thread, the operation is synchronized on the Java Thread object. So there // cannot be a race to start the thread and hence for the thread to exit while // we are working on it. Non-Java threads that start Java threads either have // to do so in a context in which races are impossible, or should do appropriate // locking. void os::start_thread(Thread* thread) { // guard suspend/resume MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag); OSThread* osthread = thread->osthread(); osthread->set_state(RUNNABLE); pd_start_thread(thread); }Copy the code

Pd_start_thread = pd_start_thread = pd_start_thread = pd_start_thread

The results are shown in figure ~

Finally, record the global function notify 😝

bool Monitor::notify(a) {
  assert (_owner == Thread::current(), "invariant");assert (ILocked(), "invariant");if (_WaitSet == NULL) return true ;
  NotifyCount ++ ;

  // Transfer one thread from the WaitSet to the EntryList or cxq.
  // Currently we just unlink the head of the WaitSet and prepend to the cxq.
  // And of course we could just unlink it and unpark it, too, but
  // in that case it'd likely impale itself on the reentry.
  Thread::muxAcquire (_WaitLock, "notify:WaitLock"); ParkEvent * nfy = _WaitSet ;if(nfy ! =NULL) {                  // DCL idiom
    _WaitSet = nfy->ListNext ;
    assert (nfy->Notified == 0."invariant");// push nfy onto the cxq
    for (;;) {
      const intptr_t v = _LockWord.FullWord ;
      assert ((v & 0xFF) == _LBIT, "invariant"); nfy->ListNext = (ParkEvent *)(v & ~_LBIT);if (CASPTR (&_LockWord, v, UNS(nfy)|_LBIT) == v) break;
      // interference - _LockWord changed -- just retry
    }
    // Note that setting Notified before pushing nfy onto the cxq is
    // also legal and safe, but the safety properties are much more
    // subtle, so for the sake of code stewardship ...
    OrderAccess::fence() ;
    nfy->Notified = 1;
  }
  Thread::muxRelease (_WaitLock) ;
  if(nfy ! =NULL && (NativeMonitorFlags & 16)) {
    // Experimental code ... light up the wakee in the hope that this thread (the owner)
    // will drop the lock just about the time the wakee comes ONPROC.
    nfy->unpark() ;
  }
  assert (ILocked(), "invariant");return true ;
}
Copy the code

Surprise to see this comment in this code, which mentions WaitSet, EntryList, and CXQ

This is an implicit lock 🔒 [[Synchronized’s internal implementation]] or [[Monitor]] 👍

  • WaitSetIs a wait queue for threads that enter the wait state
  • cxqIs a competing queue, where all threads requesting the lock 🔒 will arrive first
  • EntryListstorecxqThreads that qualify as candidate resources to compete for locks
// Transfer one thread from the WaitSet to the EntryList or cxq.
// Currently we just unlink the head of the WaitSet and prepend to the cxq.
// And of course we could just unlink it and unpark it, too, but
// in that case it'd likely impale itself on the reentry.
Thread::muxAcquire (_WaitLock, "notify:WaitLock");Copy the code

Hey hey back into the lock part to share 😝


threadStatus

Back at the top, there’s still one problem left to solve

thisthreadStatusWhat are the values?

Here we can get 👇 from the jvm.h source code

/* * Java thread state support */
enum {
    JAVA_THREAD_STATE_NEW           = 0,
    JAVA_THREAD_STATE_RUNNABLE      = 1,
    JAVA_THREAD_STATE_BLOCKED       = 2,
    JAVA_THREAD_STATE_WAITING       = 3,
    JAVA_THREAD_STATE_TIMED_WAITING = 4,
    JAVA_THREAD_STATE_TERMINATED    = 5,
    JAVA_THREAD_STATE_COUNT         = 6
};
Copy the code

conclusion

Thread Start: Thread Start: Thread Start: Thread Start: Thread Start After being beaten by Hotspot source Boss, the 4ye adventuter can only run away with a little experience ε=ε=ε=( ̄▽ ̄) hahaha 🐷)

Harvest:

  1. Learned a little C and C++ syntax hahaha

  2. Knowing the nature of thread creation, I have the following insights for Linux scenarios

  3. Discover that for every Java thread, there is a kernel thread;

  4. Can better understand the existence of user mode and kernel mode;

  5. Learn that when a thread is started, its state changes before it is started;

  6. The actual action of starting the kernel thread is to call the Monitor::notify method to wake it up 👍

  7. At the same time, a trace of Monitor mechanism is detected in this method.

  8. Saw mutex everywhere;

  9. It’s better to feel that Java is running everywhere for a single compilation haha, the JVM has already wrapped the corresponding OS

  10. Source code reading experience +1 hahaha

  11. Open a new continent: later you can also try to compile, do your own JVM 😝