wait(),notify(),notifyAll()等方法介紹
在Object.java中,定義了wait(),notify(),和notifyAll()等接口。wait()的作用是讓當前線程進入等待狀態,同時,wait()也會讓當前線程釋放它所持有的鎖。而notify()和notifyAll()的作用,則是喚醒當前對象上的等待線程,notify()是喚醒單個線程,而notifyAll()是喚醒所有的線程
Object類中關於等待/喚醒的API詳細信息如下
notify() 喚醒在此對象監視器上等待的單個線程
notifyAll()喚醒在此對象監視器上等待的所有線程
wait() 讓當前線程處於等待(阻塞)狀態,直到其他線程調用此對象的notify()方法或notifyAll()方法,當前線程被喚醒(進入"就緒狀態")
wait(long timeout)讓當前線程處於等待(阻塞)狀態,直到其他線程調用此對象的notify()方法或notifyAll()方法,當前線程被喚醒(進入"就緒狀態")或者超過我們指定的時間量 當前線程被喚醒(進入就緒狀態)
wait(long timeout,int nanos)讓當前線程處於等待(阻塞)狀態,直到其他線程調用此對象的notify()方法或notifyAll()方法,當前線程被喚醒(進入"就緒狀態")或者超過我們指定的時間量 或者其他線程中斷當前線程 當前線程被喚醒(進入就緒狀態)
2 wait()和notify()示例
package com.tuhu.filt.javadatathread;
public class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run(){
synchronized (this){
System.out.println(
Thread.currentThread().getName()
+" call notify"
);
notify();
}
}
}
class WaitTest{
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
synchronized (t1){
try{
System.out.println(
Thread.currentThread().getName()
+" start t1"
);
t1.start();
/**
* 下面這個輸出語句也是main線程調用的,調用了之後執行run方法才
* 出現那個t1 也就是t0
*/
System.out.println(
Thread.currentThread().getName()
+" wait()"
);
t1.wait();
/**
* t1阻塞之後 又把main給喚醒了 哈哈哈
*/
System.out.println(
Thread.currentThread().getName()
+" continue"
);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
我們來說明下主線程和線程t1的流程
3 wait(long timeout)和notify()
下面演示wait(long timeout)在超時情況下,線程被喚醒的情況
package com.tuhu.filt.javadatathread;
public class ThreadB extends Thread {
public ThreadB(String name){
super(name);
}
public void run(){
System.out.println(Thread.currentThread().getName()
+" run"
);
while(true){
;
}
}
}
class WaitTimeoutTest{
public static void main(String[] args) {
ThreadB t1 = new ThreadB("t1");
synchronized (t1){
try{
System.out.println(
Thread.currentThread().getName()
+" start t1"
);
t1.start();
System.out.println(
Thread.currentThread().getName()
+" call wait"
);
t1.wait(3000);
System.out.println(
Thread.currentThread().getName()
+" continue"
);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
4 wait()和notifyAll()
public class NotifyAllTest {
private static Object obj = new Object();
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
ThreadA t2 = new ThreadA("t2");
ThreadA t3 = new ThreadA("t3");
t1.start();
t2.start();
t3.start();
try {
System.out.println(Thread.currentThread().getName()+" sleep(3000)");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(obj) {
// 主線程等待喚醒。
System.out.println(Thread.currentThread().getName()+" notifyAll()");
obj.notifyAll();
}
}
static class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run() {
synchronized (obj) {
try {
// 打印輸出結果
System.out.println(Thread.currentThread().getName() + " wait");
// 喚醒當前的wait線程
obj.wait();
// 打印輸出結果
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue
結果說明:
參考下面的流程圖。
(01) 主線程中新建並且啓動了3個線程"t1", "t2"和"t3"。
(02) 主線程通過sleep(3000)休眠3秒。在主線程休眠3秒的過程中,我們假設"t1", "t2"和"t3"這3個線程都運行了。以"t1"爲例,當它運行的時候,它會執行obj.wait()等待其它線程通過notify()或額nofityAll()來喚醒它;相同的道理,"t2"和"t3"也會等待其它線程通過nofity()或nofityAll()來喚醒它們。
(03) 主線程休眠3秒之後,接着運行。執行 obj.notifyAll() 喚醒obj上的等待線程,即喚醒"t1", "t2"和"t3"這3個線程。 緊接着,主線程的synchronized(obj)運行完畢之後,主線程釋放“obj鎖”。這樣,"t1", "t2"和"t3"就可以獲取“obj鎖”而繼續運行了!
5 爲什麼notify() wait()等函數定義在object而不是在Thread中
object wait() notify() 和synchronized一樣 會對對象同步鎖 進行操作
wait()會讓當前線程進入等待狀態 進入等待狀態 所以線程應該釋放它所持有的"同步鎖",否則其他線程獲取不到該"同步鎖"而無法運行
釋放掉同步鎖之後,等待線程可以被notify()喚醒
請思考一個問題:notify()是依據什麼喚醒等待線程的?或者說,wait()等待線程和notify()之間是通過什麼關聯起來的?答案是:依據“對象的同步鎖”。
負責喚醒等待線程的那個線程(我們稱爲“喚醒線程”),它只有在獲取“該對象的同步鎖”(這裏的同步鎖必須和等待線程的同步鎖是同一個),並且調用notify()或notifyAll()方法之後,才能喚醒等待線程。雖然,等待線程被喚醒;但是,它不能立刻執行,因爲喚醒線程還持有“該對象的同步鎖”。必須等到喚醒線程釋放了“對象的同步鎖”之後,等待線程才能獲取到“對象的同步鎖”進而繼續運行。
總之,notify(), wait()依賴於“同步鎖”,而“同步鎖”是對象鎖持有,並且每個對象有且僅有一個!這就是爲什麼notify(), wait()等函數定義在Object類,而不是Thread類中的原因。