關于Thread.yield()和Thread.sleep(0)的語義問題真是一個讓人撓頭的問題,翻了好多資料,在java6語言規范中看到了一段這樣的描述:
重點在紅框中,簡而言之就是:sleep(0)和yield()的實現不需要任何可見的效果。那么在實現這兩個函數的語義時就可以什么都不做,這取決于具體的JVM實現。后來再看java8的語言規范時發現紅框內的提示被去掉了,官方給出了下面的解釋:
大致意思就是說之前的那句沒表達清楚,所以在語言規范中去掉了,最后強調的說了:java語言規范不希望yiled和spleep(0)具有很強的明確語義,可以不用實現他們。
后來又在**《java concurrency in practice》**的注釋中找到了這么一句(《java concurrency in practice》這本書是設計juc的幾個人寫的,可靠性是非常高的)
The semantics of Thread.yield and Thread.sleep(0) are undefined [JLS17.9]; the JVM is free to implement them as no-ops or treat them as scheduling hints. In particular, they are not required to have the semantics of sleep(0) on Unix systems — put the current thread at the end of the run queue for that priority, yielding to other threads of the same priority — though some JVMs implement yield in this way.
<-------------------------分割線哥哥------------------------------------------------------------------------>
翻譯:
java規范中并沒有定義Thread.yield和Thread.sleep(0)的語義。jvm可以自由的去實現他們,可以不做任何操作,也可以給系統調度器提示。特別說下,sleep(0)在Unix系統下的語義是:把線程放到運行隊列的末尾,并且讓出執行權給其他同優先級的線程,很多的jvm也是這末實現的,但并不是要求必須這末實現,你想怎么玩就怎么玩,沒人管你。
最后再來看下我們常用的hotspot中的具體實現:上面這段代碼是hotspot的源碼,由于ConvertSleepToYield的默認值為true,所以在hotspot中當sleep(0)時效果相當于yield()。會讓當前線程放棄剩余時間片,進入相同優先級線程隊列的隊尾,只有排在前面的所有同優先級線程完成調度后,它才能再次獲執行的機會。
總結
結合java doc中這兩個函數的注解總結下:
Thread.yield()和Thread.sleep(0)語義實現取決于具體的jvm虛擬機,某些jvm可能什么都不做,而大多數虛擬機會讓線程放棄剩余的cpu時間片,重新變為runnable狀態,并放到同優先級線程隊列的末尾等待cpu資源。 但是當我們調用Thread.yield()的那一刻,并不意味著當前線程立馬釋放cpu資源,這是因為獲得時間片的線程從runable切換到running仍需要一定的準備時間,這段時間當前線程仍可能運行一小段時間。