线程生命周期

本文详细介绍了Java线程的生命周期,包括新建、可运行、阻塞、无限期等待、有限期等待和终止状态,并解析了`new Thread().start()`方法在JVM内部的执行过程,涉及操作系统级别的线程创建。
摘要由CSDN通过智能技术生成

线程生命周期

一、Java线程生命周期
  1. 新建状态(NEW)
    • 线程已经被创建,但是还不准分配CPU进行运行。这个是在每一种编程状态特有的。就像在java中使用new Thread()创建一个线程对象。此时的线程就处于一个新建状态。这也就是在创建一个线程对象之后调用run方法就和运行普通的方法一样的原因。本质上都是都在调用线程中调用对象方法。
  2. 可运行状态(RUNNABLE)
    • 新创建的线程对象已经可以分配CPU,根据不同的调度算法只要有空闲的CPU分配到时候,线程就可以真正的开始运行。此时的线程在已经在操作系统被创建,只要有空闲的CPU分配过来线程就可以处于运行状态。在Java中的调用线程的的start方法一样在操作系统中创建对应的线程。
  3. 阻塞状态(BLOCKED)
    • Java中一个线程进入阻塞状态的只有这样的两个就是当线程进入使用synchronized修饰的代码块或者方法的时候。等待持有锁的线程释放锁。当持有锁的线程释放锁之后,当前阻塞的线程获取到锁线程的状态会从blocked->runnable
  4. 无限期等待状态(WAITING)。
    • 当一个线程从Runnable -> waiting存在如下的几种情况
      • 调用对象wait()方法。那要将一个线程从调用wait()而变成无限期等待的状态变为可运行状态只需要调用Object.notify()或则会Object.notifyAll()
      • 在一个线程中调用另外一个线程的join()方法。
      • 处于可运行状态的线程调用LockSupport.park()。线程的状态将会变为无限期等待。需要线程从无限期的等待中恢复调用LockSupport.unpark()
  5. 有限期等待状态(TIMED_WAITING)
    • 线程从Runnable -> timed_waiting
      • 调用Thread.sleep(long)
      • 调用Object.wait(long),状态恢复Object.notify()、Object.notifyAll()
      • 调用Object.join(long)
      • 调用LockSupport.parkNanos()。状态恢复LockSupport.unpark;
      • 调用LockSupport.parkUntil();
  6. 终止状态(TERMINATED)
    • 线程正常运行结束或者是线程由于异常退出线程都会处于终止状态。
  7. 线程状态转换示意图:在这里插入图片描述
二、new thread().start发生了啥?
  1. 在java里面创建一个线程无非两种方式如下所示:

    • 继承Thread重写run方法

      public class ThreadStudy extends Thread{
          @Override
          public void run() {
              super.run();
              System.out.println("extends Thread");
          }
      }
      
    • 实现Runnable接口

      public class RunnableStudy implements Runnable{
          @Override
          public void run() {
              System.out.println("implements Runnable");
          }
      }
      
    • 调用实现

      public class MainTest {
          public static void main(String[] args) {
              Thread t1 = new ThreadStudy();
              Thread t2 = new Thread(new RunnableStudy());
      
              t1.start();
              t2.start();
          }
      }
      
  2. 整个操作可以分为两步

    1. 执行new Thread()创建线程对象。在Thread的java源码中存在如下的静态代码块

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

      从上面的代码可知registerNatives是一个native的方法,由于是使用static关键子修饰会随着类的初始化而首先加载,在jvm中的实现如下

      // todo 在java的Thread加载的时候初始化这个类中的方法映射
      JNIEXPORT void JNICALL
      Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
      {
          (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
      }
      
      // methods是一个结构体数组,具体做的是将java中的native方法和jvm中的方法进行映射,数组如下
      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},
          {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
          {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
          {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
          {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
          {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
      };
      

      上述操作就是在执行new Thread()的时候执行操作,也相当于初始化操作。

    2. 当一个线程创建完成之后,就需要调用线程的start()方法启动线程,先看看在java中的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 */
                  }
              }
          }
      
          private native void start0();
      //从前面的代码可知最终是通过调用start0()方法实现的线程启动,而start0()方法是一个native方法,请看上面start0()正是前面做过映射方法
      
    3. 这个start0()方法映射到的方法是JVM_StartThread需要看JVM_StartThread这个方法的实现。我们在jvm.h中找到这个方法

      /*
       * java.lang.Thread
       */
      // 启动线程的接口
      JNIEXPORT void JNICALL
      JVM_StartThread(JNIEnv *env, jobject thread);
      
      // 这个方法具体的实现在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 64-bit signed, but the constructor takes
            // size_t (an unsigned type), which may be 32 or 64-bit depending on the platform.
            //  - Avoid truncating on 32-bit platforms if size is greater than UINT_MAX.
            //  - Avoid passing negative values which would result in really large stacks.
            NOT_LP64(if (size > SIZE_MAX) size = SIZE_MAX;)
            size_t sz = size > 0 ? (size_t) size : 0;
            //构造thread对象
            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'.
          native_thread->smr_delete();
          if (JvmtiExport::should_post_resource_exhausted()) {
            JvmtiExport::post_resource_exhausted(
              JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
              os::native_thread_creation_failed_msg());
          }
          THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
                    os::native_thread_creation_failed_msg());
        }
      
      #if INCLUDE_JFR
        if (Jfr::is_recording() && EventThreadStart::is_enabled() &&
            EventThreadStart::is_stacktrace_enabled()) {
          JfrThreadLocal* tl = native_thread->jfr_thread_local();
          // skip Thread.start() and Thread.start0()
          tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(thread, 2));
        }
      #endif
      
        //启动Thread线程
        Thread::start(native_thread);
      
      JVM_END
      
      
    4. jvm.cpp中的实现中使用new JavaThread(&thread_entry, sz)创建了一个线程实例,那在创建的时候到底做了啥呢?请继续向下看

      //这个在thread.c。这个是其构造函数。在构造函数中可以看到最终是通过os的create_thread创建的线程
      JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : JavaThread() {
        _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);
        // The _osthread may be NULL here because we ran out of memory (too many threads active).
        // We need to throw and OutOfMemoryError - however we cannot do this here because the caller
        // may hold a lock and all locks must be unlocked before throwing the exception (throwing
        // the exception consists of creating the exception object & initializing it, initialization
        // will leave the VM via a JavaCall and then all locks must be unlocked).
        //
        // The thread is still suspended when we reach here. Thread must be explicit started
        // by creator! Furthermore, the thread must also explicitly be added to the Threads list
        // by calling Threads:add. The reason why this is not done here, is because the thread
        // object must be fully initialized (take a look at JVM_Start)
      }
      
      
    5. os是对不同平台的抽象,其具体的实现有linux,bsd,windows等这些。在生产实践中linux使用最多。那我们就看看在linux上的实现。在os_linux.cpp中对应的方法如下

      // 在操作系统中创建thread
      bool os::create_thread(Thread* thread, ThreadType thr_type,
                             size_t req_stack_size) {
        assert(thread->osthread() == NULL, "caller responsible");
      
        // Allocate the OSThread object
        OSThread* osthread = new OSThread(NULL, NULL);
        if (osthread == NULL) {
          return false;
        }
      
        // set the correct thread state
        osthread->set_thread_type(thr_type);
      
        // Initial state is ALLOCATED but not INITIALIZED
        osthread->set_state(ALLOCATED);
      
        thread->set_osthread(osthread);
      
        // init thread attributes
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
      
        // Calculate stack size if it's not specified by caller.
        size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size);
        // In glibc versions prior to 2.7 the guard size mechanism
        // is not implemented properly. The posix standard requires adding
        // the size of the guard pages to the stack size, instead Linux
        // takes the space out of 'stacksize'. Thus we adapt the requested
        // stack_size by the size of the guard pages to mimick proper
        // behaviour. However, be careful not to end up with a size
        // of zero due to overflow. Don't add the guard page in that case.
        size_t guard_size = os::Linux::default_guard_size(thr_type);
        // Configure glibc guard page. Must happen before calling
        // get_static_tls_area_size(), which uses the guard_size.
        pthread_attr_setguardsize(&attr, guard_size);
      
        size_t stack_adjust_size = 0;
        if (AdjustStackSizeForTLS) {
          // Adjust the stack_size for on-stack TLS - see get_static_tls_area_size().
          stack_adjust_size += get_static_tls_area_size(&attr);
        } else {
          stack_adjust_size += guard_size;
        }
      
        stack_adjust_size = align_up(stack_adjust_size, os::vm_page_size());
        if (stack_size <= SIZE_MAX - stack_adjust_size) {
          stack_size += stack_adjust_size;
        }
        assert(is_aligned(stack_size, os::vm_page_size()), "stack_size not aligned");
      
        int status = pthread_attr_setstacksize(&attr, stack_size);
        if (status != 0) {
          // pthread_attr_setstacksize() function can fail
          // if the stack size exceeds a system-imposed limit.
          assert_status(status == EINVAL, status, "pthread_attr_setstacksize");
          log_warning(os, thread)("The %sthread stack size specified is invalid: " SIZE_FORMAT "k",
                                  (thr_type == compiler_thread) ? "compiler " : ((thr_type == java_thread) ? "" : "VM "),
                                  stack_size / K);
          thread->set_osthread(NULL);
          delete osthread;
          return false;
        }
      
        ThreadState state;
      
        {
          pthread_t tid;
          //通过Linux提供的pthrad创建线程
          int ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread);
      
          char buf[64];
          if (ret == 0) {
            log_info(os, thread)("Thread started (pthread id: " UINTX_FORMAT ", attributes: %s). ",
              (uintx) tid, os::Posix::describe_pthread_attr(buf, sizeof(buf), &attr));
          } else {
            log_warning(os, thread)("Failed to start thread - pthread_create failed (%s) for attributes: %s.",
              os::errno_name(ret), os::Posix::describe_pthread_attr(buf, sizeof(buf), &attr));
            // Log some OS information which might explain why creating the thread failed.
            log_info(os, thread)("Number of threads approx. running in the VM: %d", Threads::number_of_threads());
            LogStream st(Log(os, thread)::info());
            os::Posix::print_rlimit_info(&st);
            os::print_memory_info(&st);
            os::Linux::print_proc_sys_info(&st);
            os::Linux::print_container_info(&st);
          }
      
          pthread_attr_destroy(&attr);
      
          if (ret != 0) {
            // Need to clean up stuff we've allocated so far
            thread->set_osthread(NULL);
            delete osthread;
            return false;
          }
      
          // Store pthread info into the OSThread
          osthread->set_pthread_id(tid);
      
          // Wait until child thread is either initialized or aborted
          {
            Monitor* sync_with_child = osthread->startThread_lock();
            MutexLocker ml(sync_with_child, Mutex::_no_safepoint_check_flag);
            while ((state = osthread->get_state()) == ALLOCATED) {
              sync_with_child->wait_without_safepoint_check();
            }
          }
        }
      
        // The thread is returned suspended (in state INITIALIZED),
        // and is started higher up in the call chain
        assert(state == INITIALIZED, "race condition");
        return true;
      }
      

      从上述的代码可知是通过int ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread);这个语句来床架一个操作系统级别的线程。至于在操作系统上是如何实现线程的创建,奈何小弟太菜没发继续往下看。到此在jjvm层面创建一个线程就已经完成。

    6. 在线程创建完成之后,在jvm.cpp中后续使用Thread::start(native_thread);来启动线程,具体的源码如下

      // 在thread.cpp中启动的方法如下
      void Thread::start(Thread* thread) {
        // Start is different from resume in that its safety is guaranteed by context or
        // being called from a Java method synchronized on the Thread object.
        if (!DisableStartThread) {
          if (thread->is_Java_thread()) {
            // Initialize the thread state to RUNNABLE before starting this thread.
            // Can not set it after the thread started because we do not know the
            // exact thread state at that time. It could be in MONITOR_WAIT or
            // in SLEEPING or some other state.
            java_lang_Thread::set_thread_status(thread->as_Java_thread()->threadObj(),
                                                JavaThreadStatus::RUNNABLE);
          }
          os::start_thread(thread);
        }
      }
      
      // 从上述的代码可知最终是通过调用os的start_thread方法实现线程的启动,os中启动线程的方法如下
      // 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
        MutexLocker ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
        OSThread* osthread = thread->osthread();
        osthread->set_state(RUNNABLE);
        pd_start_thread(thread);
      }
      
      // 最终是通过调用pd_start_thread(thread)方法实现线程的启动,在linux中启动线程的源码如下
      void os::pd_start_thread(Thread* thread) {
        OSThread * osthread = thread->osthread();
        assert(osthread->get_state() != INITIALIZED, "just checking");
        Monitor* sync_with_child = osthread->startThread_lock();
        MutexLocker ml(sync_with_child, Mutex::_no_safepoint_check_flag);
      
        //通知前面创建的线程开始运行
        sync_with_child->notify();
      }
      
      

      到此为止在java中创建一个线程主流程就完了,但是还有很多的细节等待去补充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值