一、wait(), notify(), notifyAll()等方法介紹
1.wait()的作用是讓當(dāng)前線程進(jìn)入等待狀態(tài),同時,wait()也會讓當(dāng)前線程釋放它所持有的鎖。“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法”,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)
2.notify()和notifyAll()的作用,則是喚醒當(dāng)前對象上的等待線程;notify()是喚醒單個線程,而notifyAll()是喚醒所有的線程。
3.wait(long timeout)讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的notify()方法或 notifyAll() 方法,或者超過指定的時間量”,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)。
二、wait 的用法詳解(這里的t1是一個線程(鎖))
// main(主線程)
synchronized(t1) {
try {
t1.start();
t1.wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
// 在 t1 線程中喚醒主線程
synchronized (this) { //這里的 this 為 t1
this.notify();
}
注:
1、synchronized(t1)鎖定t1(獲得t1的監(jiān)視器)
2、synchronized(t1)這里的鎖定了t1,那么wait需用t1.wait()(釋放掉t1)
3、因為wait需釋放鎖,所以必須在synchronized中使用(沒有鎖定則么可以釋放?沒有鎖時使用會拋出IllegalMonitorStateException(正在等待的對象沒有鎖))
4. notify也要在synchronized使用,應(yīng)該指定對象,t1. notify(),通知t1對象的等待池里的線程使一個線程進(jìn)入鎖定池,然后與鎖定池中的線程爭奪鎖。那么為什么要在synchronized使用呢? t1. notify()需要通知一個等待池中的線程,那么這時我們必須得獲得t1的監(jiān)視器(需要使用synchronized),才能對其操作,t1. notify()程序只是知道要對t1操作,但是是否可以操作與是否可以獲得t1鎖關(guān)聯(lián)的監(jiān)視器有關(guān)。
5. synchronized(),wait,notify() 對象一致性
6. 在while循環(huán)里而不是if語句下使用wait(防止虛假喚醒spurious wakeup)
三、證明wait使當(dāng)前線程等待
class ThreadA extends Thread{
public ThreadA(String name) {
super(name);
}
public void run() {
synchronized (this) {
try {
Thread.sleep(1000); // 使當(dāng)前線阻塞 1 s,確保主程序的 t1.wait(); 執(zhí)行之后再執(zhí)行 notify()
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" call notify()");
// 喚醒當(dāng)前的wait線程
this.notify();
}
}
}
public class WaitTest {
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
synchronized(t1) {
try {
// 啟動“線程t1”
System.out.println(Thread.currentThread().getName()+" start t1");
t1.start();
// 主線程等待t1通過notify()喚醒。
System.out.println(Thread.currentThread().getName()+" wait()");
t1.wait(); // 不是使t1線程等待,而是當(dāng)前執(zhí)行wait的線程等待
System.out.println(Thread.currentThread().getName()+" continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
四、wait(long timeout)超時被喚醒
class ThreadA extends Thread{
public ThreadA(String name) {
super(name);
}
public void run() {
System.out.println(Thread.currentThread().getName() + " run ");
// 死循環(huán),不斷運(yùn)行。
while(true){;} // 這個線程與主線程無關(guān),無 synchronized
}
}
public class WaitTimeoutTest {
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
synchronized(t1) {
try {
// 啟動“線程t1”
System.out.println(Thread.currentThread().getName() + " start t1");
t1.start();
// 主線程等待t1通過notify()喚醒 或 notifyAll()喚醒,或超過3000ms延時;然后才被喚醒。
System.out.println(Thread.currentThread().getName() + " call wait ");
t1.wait(3000);
System.out.println(Thread.currentThread().getName() + " continue");
t1.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}