跟著sleep看jvm執行緒變化

m53469發表於2021-09-09

jvm執行緒是維護了執行緒的狀態。new,running,waiting,timed waiting,blocked,terminated。我們透過jstack等工具檢視的時候,執行緒狀態就是上面的一種。jvm本身是做了一種抽象,我們現在從一個典型的方法,來跟蹤檢視一下jvm內部又是怎麼做狀態變化的。

sleep方法入手

   public static native void sleep(long millis) throws InterruptedException;

sleep是一個native 方法,我們透過jvm原來來進行跟著(原始碼來自openjdk11)。
根據jni的規範,我們透過包名或者是jni的註冊方式找到了對應的宣告。

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 ")[[" STE, (void *)&JVM_DumpThreads},
    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

接下來我們要跟蹤的就是JVM_Sleep了。
我們一點一點來解析這個方法。
首先這裡有宏定義。

JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))

我們展開JVM_ENTRY。

#define JVM_ENTRY(result_type, header)                               
extern "C" {                                                         
  result_type JNICALL header {                                       
    JavaThread* thread=JavaThread::thread_from_jni_environment(env); 
    ThreadInVMfromNative __tiv(thread);                              
    debug_only(VMNativeEntryWrapper __vew;)                          
    VM_ENTRY_BASE(result_type, header, thread)

在ThreadInVMfromNative中,發生了一次執行緒狀態的變更。

class ThreadInVMfromNative : public ThreadStateTransition {
 public:
  ThreadInVMfromNative(JavaThread* thread) : ThreadStateTransition(thread) {
    trans_from_native(_thread_in_vm);
  }
  ~ThreadInVMfromNative() {
    trans_and_fence(_thread_in_vm, _thread_in_native);
  }
};

在構造方法中把JavaThread的**thread->thread_state()**狀態變為了_thread_in_vm。在解構函式中把狀態改成了_thread_in_native。這裡的__tiv是一個本地物件,只有在棧銷燬的時候才會觸發析構,也就是說這裡的轉為_thread_in_native只不過是一瞬間的事情。
展開頭結束後,我們再繼續往後觀察。

  JavaThreadSleepState jtss(thread);

在這個構造方法中。把**java_thread->threadObj()**的狀態變為了java_lang_Thread::SLEEPING

  static void set_thread_status(JavaThread* java_thread,
                                java_lang_Thread::ThreadStatus state) {
    java_lang_Thread::set_thread_status(java_thread->threadObj(), state);
  }

這裡對應的就是java的執行緒的狀態了。
在往下走就直接設定**thread->osthread()**的狀態為sleep。

    ThreadState old_state = thread->osthread()->get_state();
    thread->osthread()->set_state(SLEEPING);

狀態到這裡全部設定完成。

狀態梳理

透過上面的程式碼,我們可以發現最核心的就是JavaThread的這個物件,他本身代表的jvm中的執行緒狀態。會標識執行緒是在vm還是執行緒是在java或者在native。具體的狀態如下

enum JavaThreadState {
  _thread_uninitialized     =  0, // should never happen (missing initialization)
  _thread_new               =  2, // just starting up, i.e., in process of being initialized
  _thread_new_trans         =  3, // corresponding transition state (not used, included for completness)
  _thread_in_native         =  4, // running in native code
  _thread_in_native_trans   =  5, // corresponding transition state
  _thread_in_vm             =  6, // running in VM
  _thread_in_vm_trans       =  7, // corresponding transition state
  _thread_in_Java           =  8, // running in Java or in stub code
  _thread_in_Java_trans     =  9, // corresponding transition state (not used, included for completness)
  _thread_blocked           = 10, // blocked in vm
  _thread_blocked_trans     = 11, // corresponding transition state
  _thread_max_state         = 12  // maximum thread state+1 - used for statistics allocation
};

這個類同時那種java執行緒狀態的引用。就是java_thread->threadObj()。這個對應的是java的執行緒狀態,這個也是我們jstack看到的狀態。

"main" #1 prio=5 os_prio=31 tid=0x00007fee9b809000 nid=0xe03 waiting on condition [0x0000700008d65000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.company.Sleep.main(Sleep.java:7)

為什麼輸出的是timed_waiting(sleep),這個主要是因為格式化輸出的原因。

    if(status == THREAD_STATUS_NEW){
      return "NEW";
    }else if(status == THREAD_STATUS_RUNNABLE){
      return "RUNNABLE";
    }else if(status == THREAD_STATUS_SLEEPING){
      return "TIMED_WAITING (sleeping)";
    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT){
      return "WAITING (on object monitor)";
    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT_TIMED){
      return "TIMED_WAITING (on object monitor)";
    }else if(status == THREAD_STATUS_PARKED){
      return "WAITING (parking)";
    }else if(status == THREAD_STATUS_PARKED_TIMED){
      return "TIMED_WAITING (parking)";
    }else if(status == THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER){
      return "BLOCKED (on object monitor)";
    }else if(status == THREAD_STATUS_TERMINATED){
      return "TERMINATED";
    }
    return "UNKNOWN";
  }

在狀態格式化的時候,把sleeping的歸類成為TIMED_WAITING (sleeping)。
於此同時Java Thread還拿著系統的執行緒thread->osthread()。

小結

jvm中起碼有三種狀態的變化,一種是代表的java規範中的執行緒狀態(java_thread->threadObj()),一種是表示的jvm代理的系統執行緒的狀態(thread->osthread()),同時還有jvm執行緒在做轉換的一種狀態,是vm自己的狀態表示。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2819/viewspace-2825709/,如需轉載,請註明出處,否則將追究法律責任。

相關文章