Thread start 源码揭秘

简介: Thread start 源码揭秘public synchronized void start() { /** * 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 ze

Thread start 源码揭秘


public synchronized void start() {
    /**
     * 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 {
        // 看这里~
        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 */
        }
    }
}
复制代码


从上面的代码中,我们可以发现它会先去 判断  threadStatus 是不是 0, 不是的话会抛出异常 。


Thread  源码中我们可以发现 threadStatus  默认值就是 0


网络异常,图片无法展示
|


  • 那什么时候会被改变呢?


  • 这个 threadStatus  都有哪些值呢?


嘿嘿 带着小小的疑问继续往下看叭~ 😝




start0


状态的改变肯定伴随着线程的启动,所以我们直接来到下面这个 start0 方法


private native void start0();
复制代码


可以发现它是一个 native 函数,我们直接


OpenJDKJDK 源码中全局搜索,就可以找到它了


path: jdk\src\share\native\java\lang\Thread.c


网络异常,图片无法展示
|


JNINativeMethod


这个  JNINativeMethod 是一个结构体


path: 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;
复制代码


JVM_StartThread


顺着思路,我们找到这个  JVM_StartThread 方法


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


/*
 * java.lang.Thread
 */
JNIEXPORT void JNICALL
JVM_StartThread(JNIEnv *env, jobject thread);
复制代码


这里 JNIEXPORTJNICALL 都是 JNI 的关键字,表示此函数是要被 JNI 调用的


看到它是 JVM 开头的文件,我们来到下面这个 hotspot 的源码中找找看😋


发现这里有使用到👉


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


网络异常,图片无法展示
|


代码:


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");
  }
 // 看这里~
  Thread::start(native_thread);
JVM_END
复制代码


这里 JVM_ENTRYJVM_END是两个宏定义,定义了函数体的头和尾。 😄


这里太多细节了,啃不下呀 😅


网络异常,图片无法展示
|


讲几个大概的~  咳咳 也就看看作者的注释翻译翻译🐷


比如:


创建线程


native_thread = new JavaThread(&thread_entry, sz);
复制代码


来到构造器中


JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  Thread()
#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();
  _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;
  // 看这里~
   os::create_thread(this, thr_type, stack_sz);
  _safepoint_visible = false;
}
复制代码


创建内核线程


发现这里是去 创建内核线程~ 👍


os::create_thread(this, thr_type, stack_sz);
复制代码


顺着思路继续探索~


针对 linux 的场景


有如下的代码 🐂


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


终于看到有点眼熟的函数了~ 哈哈哈


int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
复制代码


网络异常,图片无法展示
|


底层内容真是让人抓狂~   无止境的..  哈哈哈


这里截下这个指针函数 java_start ,有兴趣的小伙伴继续往下冲冲冲!😄


网络异常,图片无法展示
|


启动线程


另一个重点 **Thread::start(native_thread); **


Thread::start


来到下面这里~


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


网络异常,图片无法展示
|


第一个红框的作用:


在启动该线程之前,将线程状态初始化为 RUNNABLE。


不能在线程启动后设置,因为我们不知道 正确的线程状态,它可能在


MONITOR_WAIT 或 在睡眠或其他状态。


第二个红框的作用,启动内核线程!


// 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);
}
复制代码


嘿嘿 这里就直接搜搜看 pd_start_thread 啦,毕竟一看就知道是它去启动线程的 哈哈哈 😝


结果如图~


网络异常,图片无法展示
|


最后记录下这个 全局函数 notify  😝


bool Monitor::notify() {
  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 ;
}
复制代码


惊喜,居然在这个代码中看到这段注释,这里提到  WaitSet , EntryList ,cxq


这是 隐式锁🔒  [[Synchronized 内部的实现原理呀]]  或者 称它为  [[Monitor机制]] 👍


  • WaitSet 是一个等待队列,存放进入等待状态的线程


  • cxq 是一个竞争队列,所有请求锁🔒的线程会先到这里


  • EntryList  存放 cxq  中有资格成为候选资源去竞争锁的线程


// 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") ;
复制代码


嘿嘿 后面深入锁的部分再分享😝


网络异常,图片无法展示
|




threadStatus


回到最上面,那还剩一个问题没解决~


这个 threadStatus  都有哪些值呢?


这里我们可以从 jvm.h  源码中获取到 👇


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



目录
相关文章
|
3月前
|
Java
多线程中的run方法和start方法有什么区别?
多线程中的run方法和start方法有什么区别?
|
9月前
Thread 类中的 start () 和 run () 方法的区别
Thread 类中的 start () 和 run () 方法的区别
37 0
|
10月前
|
Java
Java线程中的run()和start()区别
Java线程中的run()和start()区别
62 0
|
Java 调度
Java 最常见的面试题:线程的 run()和 start()有什么区别?
Java 最常见的面试题:线程的 run()和 start()有什么区别?
|
消息中间件 安全 Java
|
安全 Java 中间件
Thread.sleep(0)的作用
在源码中经常能看到sleep(0)的操作,今天来总结下sleep(0)的作用到底是啥
384 0
|
Java
Java Thread 中run()方法和start()方法的区别
Java Thread 中run()方法和start()方法的区别
78 0
妹子问我为啥启动线程时使用 start 而不是 run
今天团队里面的妹子问阿粉,为什么在启动线程的时候,都使用 start 方法,而不是 run 方法呢 还好阿粉平时一直有在学习,要不真的被妹子问住了 在多线程中,如果想让一个线程启动,你使用的方法一定是 thread.start() 方法,而不是 thread.run() 方法(啥,你用的不是 thread.start() 方法?乖,你的打开方式不对哦,下次不要这样了 有没有疑惑,为什么每次我们都习惯调用 start() 方法,为什么不直接调用 run() 方法来启动线程呢? 而且如果去看源码的话,你会发现,在 thread.start() 方法中,其实最后还是调用了 thread.ru
妹子问我为啥启动线程时使用 start 而不是 run