Java 中有時須要將線程進入睡眠狀態,這時通常咱們就會經過Thread.sleep
使線程進入睡眠狀態,接下去就看看執行該語句在 JVM 中作了什麼。java
如下是一個簡單的例子,使主線程睡眠5秒鐘。mysql
public class TestSleep {
public static void main(String[] args) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
複製代碼
在繼續往 JVM 層看start0
本地方法前,咱們先了解下 JVM 中的相關線程,這將有助於後面更好理解 Java 層線程與 JVM 中的線程對應關係。sql
在 JVM 中,也用 C++ 定義了一些 Thread 類,它們的繼承結構以下,其中對於 Java 層線程到 JVM 層主要相關的有 Java 層的 java.lang.Thread、JavaThread 和 OSThread。安全
--Thread
--JavaThread
--CodeCacheSweeperThread
--CompilerThread
--JvmtiAgentThread
--ServiceThread
--NamedThread
--ConcurrentGCThread
--VMThread
--WorkerThread
--AbstractGangWorker
--GCTaskThread
--WatcherThread
--OSThread
複製代碼
在 Thread 類中,sleep
是一個靜態且本地方法。bash
public static native void sleep(long millis) throws InterruptedException;
複製代碼
Java 層聲明的本地方法對應實如今 Thread.c 中,sleep
是一個註冊到 JVM 中的方法,它與 JVM 中的JVM_Sleep
函數綁定了,因此實現邏輯在JVM_Sleep
函數裏。邏輯爲:網絡
JVMWrapper("JVM_Sleep")
用於調試。JavaThreadSleepState jtss(thread)
用於修改線程狀態並作一些統計,當睡眠結束後,會修改回線程狀態,在 JavaThreadSleepState 的析構函數中修改。ConvertSleepToYield
作不一樣處理,它表示是否將 sleep 操做轉爲 yield 操做。分別調用os::naked_yield
和os::sleep
處理,封裝了不一樣操做系統的調用實現,後面以 Windows 爲例分別看相應實現。thread->osthread()->get_state()
獲取 OSThread 對象,並將其狀態設置爲SLEEPING
等到 sleep 結束後設置回原來的狀態。JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
JVMWrapper("JVM_Sleep");
if (millis < 0) {
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
}
if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
}
JavaThreadSleepState jtss(thread);
HOTSPOT_THREAD_SLEEP_BEGIN(millis);
EventThreadSleep event;
if (millis == 0) {
if (ConvertSleepToYield) {
os::naked_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);
}
} else {
ThreadState old_state = thread->osthread()->get_state();
thread->osthread()->set_state(SLEEPING);
if (os::sleep(thread, millis, true) == OS_INTRPT) {
if (!HAS_PENDING_EXCEPTION) {
if (event.should_commit()) {
event.set_time(millis);
event.commit();
}
HOTSPOT_THREAD_SLEEP_END(1);
THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
}
}
thread->osthread()->set_state(old_state);
}
if (event.should_commit()) {
event.set_time(millis);
event.commit();
}
HOTSPOT_THREAD_SLEEP_END(0);
JVM_END
複製代碼
naked_yield
函數的實現很簡單,就直接調用SwitchToThread
系統函數。經過該函數可讓系統查看是否有其餘線程迫切須要CPU,將CPU讓給其餘線程,若是沒有其餘線程則當即返回。併發
void os::naked_yield() {
SwitchToThread();
}
複製代碼
sleep
函數。Sleep
系統函數來實現。WaitForMultipleObjects
系統函數,該函數能等待指定對象指定的毫秒數。若是等待過程當中對象沒有接到任何信號,則超過指定毫秒數後返回WAIT_TIMEOUT
,若是等待過程當中對象收到信號,則提早解除等待,此時返回的值爲OS_INTRPT
,即表示被中斷了。int os::sleep(Thread* thread, jlong ms, bool interruptable) {
jlong limit = (jlong) MAXDWORD;
while (ms > limit) {
int res;
if ((res = sleep(thread, limit, interruptable)) != OS_TIMEOUT) {
return res;
}
ms -= limit;
}
assert(thread == Thread::current(), "thread consistency check");
OSThread* osthread = thread->osthread();
OSThreadWaitState osts(osthread, false /* not Object.wait() */);
int result;
if (interruptable) {
assert(thread->is_Java_thread(), "must be java thread");
JavaThread *jt = (JavaThread *) thread;
ThreadBlockInVM tbivm(jt);
jt->set_suspend_equivalent();
HANDLE events[1];
events[0] = osthread->interrupt_event();
HighResolutionInterval *phri=NULL;
if (!ForceTimeHighResolution) {
phri = new HighResolutionInterval(ms);
}
if (WaitForMultipleObjects(1, events, FALSE, (DWORD)ms) == WAIT_TIMEOUT) {
result = OS_TIMEOUT;
} else {
ResetEvent(osthread->interrupt_event());
osthread->set_interrupted(false);
result = OS_INTRPT;
}
delete phri;
jt->check_and_wait_while_suspended();
} else {
assert(!thread->is_Java_thread(), "must not be java thread");
Sleep((long) ms);
result = OS_TIMEOUT;
}
return result;
}
複製代碼
前面說到 ThreadBlockInVM 會檢查當前線程用不用進入 safepoint,它主要的邏輯以下:app
_thread_in_vm = 6
變爲_thread_in_vm_trans = 7
,從「運行vm自己代碼」到「相應的過分狀態」。os::is_MP()
用於判斷計算機系統是否爲多核系統,多核狀況下須要作內存屏障處理,這是爲了讓每一個線程都能實時同步狀態。rderAccess::fence()
,它的實現是直接經過CPU指令來實現,彙編指令爲__asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
,這種方式代價比較大。而另一種爲InterfaceSupport::serialize_memory
,由 JVM 模擬實現,效率高一點。SafepointSynchronize::block
嘗試在該安全點進行阻塞。_thread_blocked
,即阻塞。static inline void transition_and_fence(JavaThread *thread, JavaThreadState from, JavaThreadState to) {
assert(thread->thread_state() == from, "coming from wrong thread state");
assert((from & 1) == 0 && (to & 1) == 0, "odd numbers are transitions states");
thread->set_thread_state((JavaThreadState)(from + 1));
if (os::is_MP()) {
if (UseMembar) {
OrderAccess::fence();
} else {
// Must use this rather than serialization page in particular on Windows
InterfaceSupport::serialize_memory(thread);
}
}
if (SafepointSynchronize::do_call_back()) {
SafepointSynchronize::block(thread);
}
thread->set_thread_state(to);
CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();)
}
複製代碼
-------------推薦閱讀------------機器學習
個人開源項目彙總(機器&深度學習、NLP、網絡IO、AIML、mysql協議、chatbot)函數
跟我交流,向我提問:
歡迎關注: