關于 wait 方法和 notify 方法實作細節主要是根據 官方的javaDoc 文檔和 openJDK 源碼中得出,jdk 源碼位址:
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/30fb8c8cceb9/src/share/vm/runtime/objectMonitor.hpp
1. wait() 方法
1.1 調用wait 方法時,必須目前線程持有該對象的monitor
1.2 對象調用wait 方法後,它會将目前線程放入該對象的 等待集合(wait set)中,在JVM 的C++源碼中,本質是放入一個ObjectMonitor 中,裡面有一個屬性叫 _WaitSet ,就是用于存放ObjectWait 對象,
而ObjectWait 對象本質就是一個連結清單結構。ObjectMonitor 中持有該ObjectWait 的數組,源碼如下:
// ObjectWaiter serves as a "proxy" or surrogate thread.
// TODO-FIXME: Eliminate ObjectWaiter and use the thread-specific
// ParkEvent instead. Beware, however, that the JVMTI code
// knows about ObjectWaiters, so we'll have to reconcile that code.
// See next_waiter(), first_waiter(), etc.
class ObjectWaiter : public StackObj {
public:
enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ } ;
enum Sorted { PREPEND, APPEND, SORTED } ;
ObjectWaiter * volatile _next;
ObjectWaiter * volatile _prev;
Thread* _thread;
jlong _notifier_tid;
ParkEvent * _event;
volatile int _notified ;
volatile TStates TState ;
Sorted _Sorted ; // List placement disposition
bool _active ; // Contention monitoring is enabled
public:
ObjectWaiter(Thread* thread);
void wait_reenter_begin(ObjectMonitor *mon);
void wait_reenter_end(ObjectMonitor *mon);
};
// initialize the monitor, exception the semaphore, all other fields
// are simple integers or pointers
ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL;
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
1.3 隻有當對象調用了notify 或者 notifyAll 方法,又或者是等待時間逾時,該對象會到wait set 中根據不同的政策将線程從中移除,并将線程放入到 Enter list 中,與其它等待的線程共同競争該對象的鎖,一旦擷取到monitor ,該線程就會繼續往下執行。
1.4 如果目前線程被其它線程打斷,那麼它産生 InterruptedException ,當該對象重新擷取了鎖狀态,那麼該異常會抛出。
2. notify ,nofityAll 方法
2.1 調用notify 方法後,它會喚醒目前對象中正在waiting(等待)的多個線程中的一個線程。
2.2 被喚醒的線程并不會馬上進行執行它後面的程式,它需要在目前線程讓出monitor 後,被喚醒線程擷取對象的鎖後,才可以執行
2.3 被喚醒的線程并不會存在優先權或者不足,它與其它阻塞線程一樣,公平競争着對象的鎖。
2.4 持有對象的鎖有三種方式: synchronized 代碼塊中, 對象的synchronized 方法, 類中的靜态的synchronized 方法
2.5 一次隻能有一個線程持有對象的monitor
3. Thread.sleep() 方法
線程的sleep() 方法,它會将目前對象進行阻塞,并且會一直持有目前對象的鎖。
4. wait 與 sleep的差別:
1. 關于wait方法:
1.1 wait 是Object 類執行個體本地方法,它底層是由C++實作,
1.2 它隻能在目前線程獲得了對象的monitor 之後 才允許被調用 (也就是必須在synchronized 的方法中或者代碼塊中)。如果沒有在裡面進行調用,則會抛出 java.lang.IllegalMonitorStateException 異常。
注意:網上有文章說調用該方法不用捕獲異常,這是非常錯誤的結論,讀者在看時,最好自己動手确認一下。
1.3 通過将目前線程放入 等待集合中,該線程被挂起,同時釋放掉目前對象的monitor , 直到目前對象調用 nofity或者 notifyAll 方法後,才能被喚醒。
1.4被喚醒的的線程我會被移出wait set ,然後放入後到enter list 中,與其它等待線程一樣競争對象的monitor,如果拿到,則該線程則恢複原來操作繼續往下執行,否則将繼續等待。
PS:注意 wait 方法通常放入while 循環中進行,因為可能會出現假喚醒,而此時并不滿足條件 ,而讓代碼往下走。
2. 關于sleep 方法
2.1 它是Thread 類的靜态本地方法,它底層也是由C++實作,
2.2 它可以在任意地方被調用,需要異常的捕獲。
2.3 它會将目前線程進行休眠,同時并不會釋放與之相關的monitor.
2.4 它在休眠中會讓出CPU資源, 隻有休眠時間逾時後,線程恢複到可運作狀态。
2.5 當它被打斷時,會抛出 InterruptedException