看到一个问题“sleep(0)的作用是什么”,发现底层牵涉还挺多内容。
以下基于openjdk8的源码,hg id 87ee5ee27509。
回顾线程状态状态机图:
Thread.sleep()
实现: /src/share/vm/prims/jvm.cpp#l3038
if (millis == 0) { // When ConvertSleepToYield is on, this matches the classic VM implementation of // JVM_Sleep. Critical for similar threading behaviour (Win32) // It appears that in certain GUI contexts, it may be beneficial to do a short sleep // for SOLARIS if (ConvertSleepToYield) { os::yield(); } else { ThreadState old_state = thread->osthread()->get_state(); thread->osthread()->set_state(SLEEPING); os::sleep(thread, MinSleepInterval, false); thread->osthread()->set_state(old_state); } } // more code
Thread.yield()
实现: /src/share/vm/prims/jvm.cpp#l3002
JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass)) JVMWrapper("JVM_Yield"); if (os::dont_yield()) return; #ifndef USDT2 HS_DTRACE_PROBE0(hotspot, thread__yield); #else /* USDT2 */ HOTSPOT_THREAD_YIELD(); #endif /* USDT2 */ // When ConvertYieldToSleep is off (default), this matches the classic VM use of yield. // Critical for similar threading behaviour if (ConvertYieldToSleep) { os::sleep(thread, MinSleepInterval, false); } else { os::yield(); } JVM_END
sleep(0)和yield的底层实现行为,由变量 ConvertSleepToYield
和 ConvertYieldToSleep
控制。并且使用平台相关的 os::yield()
或者 os::sleep()
。
os::sleep
源码: /src/os/linux/vm/os_linux.cpp#l3792
原码100来行,不贴了,简单说下:
interruptible
。 os::is_interrupted(thread, true)
,或者时间到 millis <= 0
。否则通过 ParkEvent.park(millis)
阻塞等待millis之后返回,进入下一次循环。
ParkEvent * const slp = thread->_SleepEvent ; // more code prevtime = newtime; { assert(thread->is_Java_thread(), "sanity check"); JavaThread *jt = (JavaThread *) thread; ThreadBlockInVM tbivm(jt); OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */); jt->set_suspend_equivalent(); // cleared by handle_special_suspend_equivalent_condition() or // java_suspend_self() via check_and_wait_while_suspended() slp->park(millis); // were we externally suspended while we were waiting? jt->check_and_wait_while_suspended(); }
以后再对 ParkEvent
做介绍,此处不展开。
os::yield
实现同样在os_linux.cpp
int os::naked_sleep() { // %% make the sleep time an integer flag. for now use 1 millisec. return os::sleep(Thread::current(), 1, false); } void os::yield() { sched_yield(); }
找到 SCHED_YIELD
sched_yield() causes the calling thread to relinquish the CPU. The thread is moved to the end of the queue for its static priority and a new thread gets to run.
linux平台上:
源码连接: /src/os/windows/vm/os_windows.cpp
比较有意思的是Windows系统的yield和sleep的实现。
见 Sleep function
A value of zero causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread continues execution.Windows XP: A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution. This behavior changed starting with Windows Server 2003.
sleep(0)会使该thread放弃拥有的剩余时间片。
.net 4.0版本以后增加了 Thread.Yield Method
If this method succeeds, the rest of the thread’s current time slice is yielded. The operating system schedules the calling thread for another time slice, according to its priority and the status of other threads that are available to run. Yielding is limited to the processor that is executing the calling thread. The operating system will not switch execution to another processor, even if that processor is idle or is running a thread of lower priority. If there are no other threads that are ready to execute on the current processor, the operating system does not yield execution, and this method returns false. This method is equivalent to using platform invoke to call the native Win32 SwitchToThread function. You should call the Yield method instead of using platform invoke, because platform invoke bypasses any custom threading behavior the host has requested.
注意yield的时间片只能被同一个处理的线程使用。
Windows平台: