Java多線程的wait(),notify(),notifyAll()、sleep()和yield()方法使用詳解 ...
Java多線程中的wait(),notify(),notifyAll()、sleep()和yield()方法
我們先從一個案例開始:
static public class WaitingTest {
//static創建靜態對象,確保是同一個monitor對象
static Object obj = new Object();
public static void main(String[] args) {
//new Thread(Runnable target, String name)
//第一個線程,匿名內部類形式創建,name:"等待線程"
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
synchronized (obj) {
System.out.println(Thread.currentThread().getName()+
"===>獲取到鎖對象,調用wait方法,進入waiting狀態,釋放鎖對象");
try {
obj.wait(); //無限等待,如果想喚醒一個正在等待的線程,那麼需要開啟一個線程通過notify()或者notifyAll()方法去通知正在等待的線程獲取monitor對象。notify()喚醒一個wait對象,或者notifyAll喚醒所有wait對象。
} catch (InterruptedException e) {
e.printStackTrace();
}
//若沒有notify()喚醒,則下麵這句話永遠不執行
System.out.println(Thread.currentThread().getName()+
"=====>從waiting狀態醒來,獲得了鎖對象,可以繼續執行了...");
}
}
}
},"等待線程").start();
//第二個線程,同樣也是匿名內部類形式創建,name:"喚醒線程"
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName()+"=====>休眠三秒鐘");
try {
Thread.sleep(3000L);//雖然當前線程進入了睡眠狀態,但是依然持有monitor對象。在中斷完成之後,自動進入喚醒狀態從而繼續執行代碼
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {
System.out.println(Thread.currentThread().getName()+
"=====>獲得了鎖對象,調用notify方法,釋放鎖對象");
obj.notify();
}
}
}
},"喚醒線程").start();
}
}
執行結果:
等待線程===>獲取到鎖對象,調用wait方法,進入waiting狀態,釋放鎖對象
喚醒線程=====>休眠三秒鐘
喚醒線程=====>獲得了鎖對象,調用notify方法,釋放鎖對象
喚醒線程=====>休眠三秒鐘
等待線程=====>從waiting狀態醒來,獲得了鎖對象,可以繼續執行了...
等待線程===>獲取到鎖對象,調用wait方法,進入waiting狀態,釋放鎖對象
喚醒線程=====>獲得了鎖對象,調用notify方法,釋放鎖對象
喚醒線程=====>休眠三秒鐘
等待線程=====>從waiting狀態醒來,獲得了鎖對象,可以繼續執行了...
等待線程===>獲取到鎖對象,調用wait方法,進入waiting狀態,釋放鎖對象
wait()方法:
(1)調用了wait()之後當前線程會處於等待狀態(無限等待狀態)。
(2)每個線程必須持有該對象的monitor(Object's monitor)。如果在當前線程中調用wait()方法之後,該線程就會釋放monitor的持有對象並讓自己處於等待狀態。
(3)如果想喚醒一個正在等待的線程,那麼需要開啟一個線程通過notify()或者notifyAll()方法去通知正在等待的線程獲取monitor對象。當然,需要註意的一點就是,必須是同一個monitor對象。如此,該線程即可打破等待的狀態繼續執行代碼。
notify()方法:
(1)當一個線程處於wait()狀態時,也即等待它之前所持有的object's monitor被釋放,通過notify()方法可以讓該線程重新處於活動狀態,從而去搶奪object's monitor,喚醒該線程。
(2)如果多個線程同時處於等待狀態,那麼調用notify()方法只能隨機喚醒一個線程。
(3)在同一時間內,只有一個線程能夠獲得object's monitor,執行完畢之後,則再將其釋放供其它線程搶占。
notifyAll()方法:
(1)notifyAll()只會喚醒那些等待搶占指定object's monitor的線程,其他線程則不會被喚醒。
(2)notifyAll()只會一個一個的喚醒,而並非統一喚醒。因為在同一時間內,只有一個線程能夠持有object's monitor
(3)notifyAll()只是隨機的喚醒線程,並非有序喚醒。
總結:
wait(),notify(),notifyAll()不屬於Thread類,而是屬於Object基礎類,也就是說每個對像都有wait(),notify(),notifyAll()的功能。因為都個對像都有鎖,鎖是每個對像的基礎,當然操作鎖的方法也是最基礎了。
sleep()方法:sleep(long millis) sleep(long millis, int nanos)
(1)調用sleep()之後,會引起當前執行的線程進入暫時中斷狀態,睡眠狀態,即阻塞狀態。
(2)雖然當前線程進入了阻塞狀態,但是依然持有monitor對象。
(3)在中斷完成之後,自動進入就緒狀態。sleep()方法雖然會使線程中斷,但是不會將自己的monitor對象釋放,在中斷結束後,依然能夠保持代碼繼續執行。
yield()方法:線程讓步
(1)使線程從運行狀態到就緒狀態,所以yield()方法很有可能剛執行完又繼續獲取到CPU資源繼續執行。
(2)調用yield方法會讓當前線程交出CPU許可權,讓CPU去執行其他的線程,即讓系統線程調度器重新調度。
(3)它跟sleep方法類似,同樣不會釋放鎖。但是yield不能控制具體的交出CPU的時間(沒有形參)。
(4)yield()只會讓同優先順序或更高優先順序得到執行機會,而sleep()是無限制的。
最後附一張我自己的手繪多線程生命周期圖(字有點醜,請見諒)
如有錯誤,歡迎指點