線程的等待與喚醒
在Object.java中原在,定義了wait(), notify()和notifyAll()等接口。wait()的作用是讓當(dāng)前線程進(jìn)入等待狀態(tài),同時(shí),wait()也會(huì)讓當(dāng)前線程釋放它所持有的鎖士败。而notify()和notifyAll()的作用,則是喚醒當(dāng)前對(duì)象上的等待線程褥伴;notify()是喚醒單個(gè)線程谅将,而notifyAll()是喚醒所有的線程。
Object類中關(guān)于等待/喚醒的API詳細(xì)信息如下:
- notify():隨機(jī)喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線程重慢。
- notifyAll():?jiǎn)拘言诖藢?duì)象監(jiān)視器上等待的所有線程饥臂。
- wait():讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法”似踱,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)隅熙。
- wait(long timeout):讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”稽煤,“直到其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法,或者超過(guò)指定的時(shí)間量”囚戚,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)酵熙。
- wait(long timeout, int nanos):讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法弯淘,或者其他某個(gè)線程中斷當(dāng)前線程绿店,或者已超過(guò)某個(gè)實(shí)際時(shí)間量”吉懊,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)庐橙。
示例1:wait() 和 notify()方法
public class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run(){
synchronized (this){
System.out.println(Thread.currentThread().getName() + " call notify()");
//喚醒當(dāng)前的wait線程
notify();
}
yield();
System.out.println(Thread.currentThread().getName() + " is running.");
}
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
ThreadA t2 = new ThreadA("t2");
synchronized (t1){
try{
//啟動(dòng)線程t1,t2
System.out.println(Thread.currentThread().getName() + " start t1");
t1.start();
System.out.println(Thread.currentThread().getName() + " start t2");
t2.start();
//主線程等待t1通過(guò)notify()喚醒
System.out.println(Thread.currentThread().getName() + " wait()");
t1.wait();
System.out.println(Thread.currentThread().getName() + " continue");
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
運(yùn)行結(jié)果:
main start t1
main start t2
main wait()
t2 call notify()
t1 call notify()
main continue
t2 is running.
t1 is running.
幾點(diǎn)說(shuō)明:
- wait()是讓“當(dāng)前線程”等待
- wait() 相當(dāng)于 wait(0)方法借嗽,表示無(wú)限等待
- “當(dāng)前線程”在調(diào)用wait()方法時(shí)态鳖,必須擁有對(duì)象的同步鎖,該線程調(diào)用完wait()方法后恶导,釋放該鎖
==========
示例2:wait(long timeout) 方法
public class ThreadB extends Thread{
public ThreadB(String name){
super(name);
}
public void run(){
System.out.println(Thread.currentThread().getName() + " is running");
//無(wú)限循環(huán)
while(true)
;
}
public static void main(String[] args) {
ThreadB threadB = new ThreadB("threadB");
synchronized (threadB){
try {
//啟動(dòng)線程threadB
System.out.println(Thread.currentThread().getName() + " start threadB");
threadB.start();
//主線程等待threadB通過(guò)notify()浆竭,或notifyAll(),或超過(guò)3000ms; 喚醒
System.out.println(Thread.currentThread().getName() + " call wait");
threadB.wait(3000);
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
運(yùn)行結(jié)果:
main start threadB
main call wait
threadB is running
main continue
Process finished with exit code 130 (interrupted by signal 2: SIGINT)
==========
示例3:wait() 和 notifyAll()方法
public class NotifyAllTest {
private static Object obj = new Object();
static class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run(){
synchronized (obj){
try{
System.out.println(Thread.currentThread().getName() + " wait");
//當(dāng)前線程等待喚醒
obj.wait();
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
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();
}
}
}
運(yùn)行結(jié)果:
t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue
幾點(diǎn)說(shuō)明:
負(fù)責(zé)喚醒等待線程的那個(gè)線程(我們稱為“喚醒線程”),它只有在獲取“該對(duì)象的同步鎖”(這里的同步鎖必須和等待線程的同步鎖是同一個(gè))惨寿,并且調(diào)用notify()或notifyAll()方法之后邦泄,才能喚醒等待線程。
notify(), wait()依賴于“同步鎖”裂垦,而“同步鎖”是對(duì)象鎖持有顺囊,并且每個(gè)對(duì)象有且僅有一個(gè)!這就是為什么notify(), wait()等函數(shù)定義在Object類蕉拢,而不是Thread類中的原因特碳。