国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

JVM源碼剖析之Thread類中sleep方法

這篇具有很好參考價值的文章主要介紹了JVM源碼剖析之Thread類中sleep方法。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

版本信息:
jdk版本:jdk8u40

寫在前面:

大部分的Java程序員知道讓線程睡眠的方法是Thread.sleep方法,而這個方法是一個native方法,讓很多想知道底層如何讓線程睡眠的程序員望而卻步。所以筆者特意寫在這篇文章,帶各位讀者剖析一下Thread.sleep方法背后的神秘。

源碼剖析:

話不多說,先從Java層面看一下sleep這個方法。

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

public static void sleep(long millis, int nanos)
throws InterruptedException {
	// 非法邏輯
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    // 非法邏輯
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }

    // 如果大于500000就算一毫秒,如果沒有設(shè)置毫秒,那么納秒單位就四舍五入算一毫秒。
    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    // 調(diào)用重載的sleep方法。
    sleep(millis);
}

這是一個重載的方法,可以單獨(dú)傳入毫秒,也可以傳入毫秒和納秒。不管調(diào)用哪一個sleep最終都是調(diào)用native的sleep方法,所以接下來需要看底層如何對其實(shí)現(xiàn)。

src/share/native/java/lang/Thread.c 文件中有定義sleep的native實(shí)現(xiàn)方法。

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

這里是一個Thread類中所有native方法的映射表,我們看到sleep映射為JVM_Sleep方法。

所以看到 src/share/vm/prims/jvm.cpp 文件中?JVM_Sleep方法

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

  // 改變狀態(tài)為sleeping中。
  JavaThreadSleepState jtss(thread);

  EventThreadSleep event;

  if (millis == 0) {		
  	// 如果傳入的毫秒為0,那么底層為轉(zhuǎn)換為yield方法,而yield僅僅是讓出CPU的使用權(quán),讓當(dāng)前線程重新等待被調(diào)度
    if (ConvertSleepToYield) {
      os::yield();
    } else {
    	// 如果不支持轉(zhuǎn)換為yield方法,那么會給出一個默認(rèn)的睡眠時間。
      ThreadState old_state = thread->osthread()->get_state();
      thread->osthread()->set_state(SLEEPING);
      os::sleep(thread, MinSleepInterval, false);
      thread->osthread()->set_state(old_state);
    }
  } else {
    // 拿到線程在sleep之前的狀態(tài)。
    ThreadState old_state = thread->osthread()->get_state();
    // 把線程狀態(tài)改變成SLEEPING
    thread->osthread()->set_state(SLEEPING);

    // 因為對于線程的操作只能交給操作系統(tǒng)
    if (os::sleep(thread, millis, true) == OS_INTRPT) {
     
      // 如果睡眠期間被中斷,那么拋出中斷異常。
      THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
    }
    // 改回之前的狀態(tài)。
    thread->osthread()->set_state(old_state);
  }
JVM_END

對這里做一個簡單的總結(jié):

  1. 改變狀態(tài)為Sleeping
  2. 如果開發(fā)者傳入的毫秒為0,這里會根據(jù)策略轉(zhuǎn)換成yield,如果不支持轉(zhuǎn)換就會給出默認(rèn)的睡眠時間
  3. 因為對于線程的操作只能交給操作系統(tǒng)完成,所以這里調(diào)用os::sleep方法,接下來會重點(diǎn)分析此方法。
  4. 如果睡眠過程中被中斷了,那么會拋出中斷異常
  5. 睡眠正常完成后,會把狀態(tài)改變成之前的狀態(tài)。

因為我們只關(guān)心Linux操作系統(tǒng),所以看到src/os/linux/vm/os_linux.cpp 文件中sleep方法。

int os::sleep(Thread* thread, jlong millis, bool interruptible) {
  ParkEvent * const slp = thread->_SleepEvent ;
  slp->reset() ;
  OrderAccess::fence() ;

  // 判斷是否響應(yīng)中斷。
  if (interruptible) {
    // 拿到進(jìn)入之前的時間(納米為單位)
    jlong prevtime = javaTimeNanos();

    for (;;) {
      // 如果被中斷了。
      if (os::is_interrupted(thread, true)) {
        return OS_INTRPT;
      }

      // 拿到最新的時間(納米為單位)
      jlong newtime = javaTimeNanos();

      
      if (newtime - prevtime < 0) {
        // 最新的時間小于之前的時間,這不是扯淡么。
        assert(!Linux::supports_monotonic_clock(), "time moving backwards");
      } else {
        // 一秒 = 1000毫秒
        // 一秒 = 1000000000納秒
        // NANOSECS_PER_MILLISEC = 1000000
        // 這里是獲取到當(dāng)前睡眠的時間,并且從納秒轉(zhuǎn)換成毫秒。
        millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
      }

      // 時間到了,直接退出。
      if(millis <= 0) {
        return OS_OK;
      }

      prevtime = newtime;

      {
        JavaThread *jt = (JavaThread *) thread;
        ThreadBlockInVM tbivm(jt);

        // 改變線程狀態(tài)。
        OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */);

        jt->set_suspend_equivalent();

        // 睡眠
        slp->park(millis);

      }
    }
  } else {
    OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
    jlong prevtime = javaTimeNanos();

    for (;;) {
      jlong newtime = javaTimeNanos();

      if (newtime - prevtime < 0) {
        assert(!Linux::supports_monotonic_clock(), "time moving backwards");
      } else {
        millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
      }

      if(millis <= 0) break ;

      prevtime = newtime;
      slp->park(millis);
    }
    return OS_OK ;
  }
}

對這里做一個簡單的總結(jié):

  1. 拿到當(dāng)前線程對應(yīng)的parkEvent,這個可以理解為提供了底層睡眠和阻塞的API。
  2. 判斷是否可以響應(yīng)中斷
  3. 如果響應(yīng)中斷,那么每次循環(huán)都會判斷是否被中斷了
  4. 獲取當(dāng)前時間,此時間是納秒
  5. 納秒轉(zhuǎn)換成毫秒,因為底層睡眠時間需要時毫秒單位(這里為什么獲取當(dāng)前時間不直接拿毫秒,因為考慮到精準(zhǔn)度的問題)
  6. 調(diào)用parkEvent的park方法,進(jìn)入操作系統(tǒng)睡眠。

考慮到文章的篇幅問題,parkEvent的park方法就不細(xì)追了。大家可以黑盒的理解,它就是讓當(dāng)前線程去阻塞,而傳入的單位就是阻塞的時間。

總結(jié):

sleep的底層實(shí)現(xiàn)并不復(fù)雜,但是不看源碼是不會知道,如果傳入的時間為0會優(yōu)化成yield方法,并且在底層并不會像Object類中wait方法一樣,釋放鎖資源等等~文章來源地址http://www.zghlxwxcb.cn/news/detail-727597.html

到了這里,關(guān)于JVM源碼剖析之Thread類中sleep方法的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • JVM源碼剖析之SymbolTable和StringTable

    JVM源碼剖析之SymbolTable和StringTable

    很多讀者在觀看JVM相關(guān)的書籍時會看到SymbolTable和StringTable,書中的三言二語介紹的不是很清楚,并且讀者的水平有限,導(dǎo)致無法理解SymbolTable和StringTable。所以特意寫此篇圖文并茂的文章來徹底理解SymbolTable和StringTable這兩張表。 版本信息如下: 因為Hotspot是c++構(gòu)成,所以也存

    2024年02月13日
    瀏覽(10)
  • 【C++】:STL源碼剖析之vector類容器的底層模擬實(shí)現(xiàn)

    【C++】:STL源碼剖析之vector類容器的底層模擬實(shí)現(xiàn)

    構(gòu)造一個空vector size和capacity為0 將_start _finish _endofstorage 都置為空指針即可 傳統(tǒng)寫法 : 1). 新開辟一塊和 v 同樣容量的空間,更新 _start, _finish, _endofstorage 2). 將 v 中的數(shù)據(jù)拷貝到新開辟的空間中 注意 : 不要使用memcpy函數(shù)拷貝數(shù)據(jù),如果數(shù)據(jù)是內(nèi)置類型或淺拷貝的自定義類型

    2024年02月04日
    瀏覽(34)
  • 單元測試使用Thread.sleep()后線程直接停止

    單元測試使用Thread.sleep()后線程直接停止

    單元測試中測試多線程,使用sleep()阻塞線程,但是運(yùn)行后發(fā)現(xiàn)Thread.sleep()后的代碼不執(zhí)行,直接退出了線程。 在單元測試中,如果子線程處于阻塞、死亡狀態(tài)時,單元測試會立刻停止所有子線程。 如下圖,不會輸出running

    2024年02月13日
    瀏覽(14)
  • 01-從JDK源碼級別剖析JVM類加載機(jī)制

    01-從JDK源碼級別剖析JVM類加載機(jī)制

    上一篇:JVM虛擬機(jī)調(diào)優(yōu)大全 當(dāng)我們用java命令運(yùn)行某個類的main函數(shù)啟動程序時,首先需要通過類加載器把主類加載到JVM。 通過Java命令執(zhí)行代碼的大體流程如下: 其中l(wèi)oadClass的類加載過程有如下幾步: 加載 驗證 準(zhǔn)備 解析 初始化 使用 卸載 加載:在硬盤上查找并通過IO讀入字

    2024年02月09日
    瀏覽(15)
  • Java中TreeSet的基本介紹,細(xì)節(jié)討論,使用注意事項,常用方法,底層源碼分析

    TreeSet 是 Java 中的一個有序集合實(shí)現(xiàn),它基于紅黑樹數(shù)據(jù)結(jié)構(gòu)來存儲元素, 可以保持元素的自然順序(默認(rèn)情況下升序)或者根據(jù)自定義比較器來進(jìn)行排序 。下面是關(guān)于 TreeSet 的基本介紹、細(xì)節(jié)討論、使用注意事項、常用方法以及一些底層實(shí)現(xiàn)細(xì)節(jié)。 基本介紹: TreeSet 是

    2024年02月11日
    瀏覽(35)
  • jvm深入研究文檔--java中的堆--詳解!--jvm底層探索(1)

    jvm深入研究文檔--java中的堆--詳解!--jvm底層探索(1)

    JVM的內(nèi)存分區(qū)包括以下幾個部分: 堆區(qū)(Heap) - 這是JVM的主要部分,用于存儲實(shí)例對象和大多數(shù)Java對象,如數(shù)組和用戶定義的類。 方法區(qū)(Method Area) - 這是線程私有的,用于存放類對象(加載好的類)。 棧區(qū)(Stack) - 這是線程私有的,包括虛擬機(jī)棧和本地方法棧。虛擬

    2024年02月07日
    瀏覽(32)
  • [.NET學(xué)習(xí)筆記] - Thread.Sleep與Task.Delay在生產(chǎn)中應(yīng)用的性能測試

    [.NET學(xué)習(xí)筆記] - Thread.Sleep與Task.Delay在生產(chǎn)中應(yīng)用的性能測試

    有個 Service 類,自己在內(nèi)部實(shí)現(xiàn) 生產(chǎn)者/消費(fèi)者 模式。即多個指令輸入該服務(wù)后對象后, Service 內(nèi)部有專門的消費(fèi)線程執(zhí)行傳入的指令。每個指令的執(zhí)行間隔為 1秒 。這里有兩部分組成, 工作線程的載體。 new Thread 與 Task.Run 。 執(zhí)行等待的方法。 Thread.Sleep 與 Task.Delay 。 cpu

    2024年02月09日
    瀏覽(19)
  • thread類中構(gòu)造的函數(shù)參數(shù)必須是可拷貝的

    錯誤代碼 這段代碼會導(dǎo)致編譯錯誤,因為在C++中,如果你嘗試在線程(std::thread)中傳遞參數(shù),那么這些參數(shù)必須是可拷貝的,或者你需要使用 std::ref 來傳遞可引用的參數(shù)。 在你的代碼中,你嘗試在線程中傳遞一個整數(shù) 3 和一個 std::string 引用 s,這是不允許的,因為 std::t

    2024年02月06日
    瀏覽(22)
  • Linux源碼解讀系列是一套深入剖析Linux內(nèi)核源碼的教程,旨在幫助讀者理解Linux操作系統(tǒng)的底層原理和工作機(jī)制

    Linux源碼解讀系列是一套深入剖析Linux內(nèi)核源碼的教程,旨在幫助讀者理解Linux操作系統(tǒng)的底層原理和工作機(jī)制

    Linux源碼解讀系列是一套深入剖析Linux內(nèi)核源碼的教程,旨在幫助讀者理解Linux操作系統(tǒng)的底層原理和工作機(jī)制。該系列教程從Linux內(nèi)核的各個模塊入手,逐一分析其源碼實(shí)現(xiàn),并結(jié)合實(shí)際應(yīng)用場景進(jìn)行講解。通過學(xué)習(xí)本系列,讀者可以深入了解Linux操作系統(tǒng)的底層機(jī)制,掌握

    2024年01月21日
    瀏覽(26)
  • String類中的一些常用方法(JAVA)

    String類中的一些常用方法(JAVA)

    目錄 字符串比較方法: boolean equals(Object anObject): ?int compareTo(String s): int compareToIgnoreCase(String str) 字符串查找方法: char charAt(int index): int indexOf(int ch): ?int indexOf(int ch, int fromIndex): int indexOf(String str): int indexOf(String str, int fromIndex): int lastIndexOf(int ch): int lastIndexOf(in

    2024年02月07日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包