关于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仍需要一定的准备时间,这段时间当前线程仍可能运行一小段时间。