跟着sleep看jvm线程变化

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);
  }
};

在构造方法中把JavaThreadthread->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自己的状态表示。


欢迎关注公众号:肥宅英短

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,492评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,048评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,927评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,293评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,309评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,024评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,638评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,546评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,073评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,188评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,321评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,998评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,678评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,186评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,303评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,663评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,330评论 2 358

推荐阅读更多精彩内容