深入理解Thread.sleep(0)的作用
Thread.sleep
先回顧一下Thread.sleep(n)方法的用法,sleep只是去休眠n毫秒的時間,當(dāng)前對象并沒有釋放掉鎖,與Object.wait()方法最大的區(qū)別就在這,wait方法會釋放掉鎖,而sleep不會。
如此看來sleep(n)中的n是休眠n毫秒,那么n=0的時候理論上說毫無意義,但有時候閱讀源碼的時候又能看到這句。寫中間件的大佬豈能犯這種錯誤。且聽我細(xì)細(xì)道來
垃圾回收
還是要從垃圾回收說起,剛開始背垃圾回收八股文的時候根本都沒想過JVM在什么時候回收辣雞,直到練習(xí)時長兩年班之后,在某個公眾號推文上才知道,JVM有一個安全區(qū)域和安全點 safepoint的概念。
其實不難理解,程序跑著跑著代碼里一個虛引用還沒來及用呢,JVM突然回收直接給干沒了,那肯定會出大問題。所以說要有一個合適的時機去做gc。
程序執(zhí)行時并非在所有地方都能停頓下來開始GC,只有在特定的位置才能停頓下來開始GC,這些位置稱為"安全點(Safepoint)"
如何在GC發(fā)生時,檢查所有線程都跑到最近的安全點停頓下來呢?
- 搶先式中斷:(目前沒有虛擬機采用了) 首先中斷所有線程。如果還有線程不在安全點,就恢復(fù)線程,讓線程跑到安全點。
- 主動式中斷: 設(shè)置一個中斷標(biāo)志,各個線程運行到SafePoint的時候主動輪詢這個標(biāo)志,如果中斷標(biāo)志為真,則將自己進行中斷掛起。
安全區(qū)域是指一段代碼片中,引用關(guān)系不會發(fā)生變化,在這個區(qū)域任何地方GC都是安全的,安全區(qū)域可以看做是安全點的一個擴展。線程執(zhí)行到安全區(qū)域的代碼時,首先標(biāo)識自己進入了安全區(qū)域,這樣GC時就不用管進入安全區(qū)域的線程了,線層要離開安全區(qū)域時就檢查JVM是否完成了GC Roots枚舉,如果完成就繼續(xù)執(zhí)行,如果沒有完成就等待直到收到可以安全離開的信號
安全點完美的解決了如何進入GC問題,實際情況可能比這個更復(fù)雜,但是如果程序長時間不執(zhí)行,比如線程調(diào)用的sleep方法,這時候程序無法響應(yīng)JVM中斷請求這時候線程無法到達安全點,顯然JVM也不可能等待程序喚醒,這時候就需要安全區(qū)域了。
sleep(0)
理解了程序垃圾回收的時機再回過頭來就不難理解sleep(0)的作用了,除了主動放棄調(diào)度之外,可以簡單的理解為程序不是任意時刻都能gc,需要程序跑到safepoint點,而native方法執(zhí)行以后就會插入一個safepoint,此時線程在執(zhí)行JVM以外的代碼不能對JVM的執(zhí)行狀態(tài)做修改,所以JVM進入safepoint可以忽略此線程。
也就是說sleep(0)的作用:
- 是主動讓出cpu調(diào)度提升系統(tǒng)調(diào)度性能
- 是使系統(tǒng)進入safepoint標(biāo)記gc。
使用場景:
Thread.sleep(0) 的使用場景非常有限,通常只在以下情況下考慮:
- 測試或調(diào)試: 在多線程測試或調(diào)試中,可以使用 Thread.sleep(0) 來強制觸發(fā)線程切換,以便觀察不同線程的執(zhí)行順序或行為。
- 某些底層優(yōu)化: 在極少數(shù)情況下,某些底層代碼可能會使用 Thread.sleep(0) 來嘗試優(yōu)化線程調(diào)度,例如,避免某個線程長時間占用 CPU 而導(dǎo)致其他線程饑餓。
- 實時系統(tǒng)(罕見): 在某些對實時性要求非常高的系統(tǒng)中,可能會使用 Thread.sleep(0) 來嘗試更精細(xì)地控制線程調(diào)度。
代碼示例:
public class ThreadSleepZeroExample { public static void main(String[] args) { Thread thread1 = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println("Thread 1: " + i); if (i == 2) { Thread.sleep(0); // 觸發(fā)線程調(diào)度 } } }); Thread thread2 = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println("Thread 2: " + i); } }); thread1.start(); thread2.start(); } }
與 Thread.yield() 的區(qū)別:
實現(xiàn)方式:
Thread.yield() 是一個 Java API 調(diào)用,它的具體實現(xiàn)依賴于 JVM 和底層操作系統(tǒng)。
Thread.sleep(0) 在大多數(shù)操作系統(tǒng)上會被實現(xiàn)為一個非常短時間的睡眠(例如,1 毫秒或更短),或者直接觸發(fā)一次線程上下文切換。
行為:
Thread.yield() 僅僅是給調(diào)度器一個提示,表示當(dāng)前線程愿意讓出 CPU。調(diào)度器可以忽略這個提示,繼續(xù)讓當(dāng)前線程執(zhí)行。
Thread.sleep(0) 由于涉及到了系統(tǒng)調(diào)用,它更可能導(dǎo)致實際的線程切換,因為它要求操作系統(tǒng)進行處理(即使時間很短)。雖然在實際上還是取決于操作系統(tǒng)的實現(xiàn),不能百分百保證,但是會讓步CPU的概率更大。
到此這篇關(guān)于深入理解Thread.sleep(0)的作用的文章就介紹到這了,更多相關(guān)Thread.sleep(0)作用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于@Valid和@Validated驗證List集合的踩坑記錄
這篇文章主要介紹了基于@Valid和@Validated驗證List集合的踩坑記錄,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07Spring Boot中的那些條件判斷的實現(xiàn)方法
這篇文章主要介紹了Spring Boot中的那些條件判斷的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04SpringBoot多數(shù)據(jù)源配置詳細(xì)教程(JdbcTemplate、mybatis)
這篇文章主要介紹了SpringBoot多數(shù)據(jù)源配置詳細(xì)教程(JdbcTemplate、mybatis),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03使用springboot時,解決@Scheduled定時器遇到的問題
這篇文章主要介紹了使用springboot時,解決@Scheduled定時器遇到的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11mybatis resultType自帶數(shù)據(jù)類型別名解讀
MyBatis為了簡化開發(fā),通過org.apache.ibatis.type.TypeAliasRegistry為常見類定義了別名,這些別名包括基本數(shù)據(jù)類型及其數(shù)組、集合類型等,如string對應(yīng)java.lang.String,int對應(yīng)java.lang.Integer等,此外,還有特殊前綴的別名如_int對應(yīng)int類型2024-10-10