初步理解
wait()
- 使線程停止運行,進入等待隊列,會立刻釋放對象鎖。
- wait(0) 代表的是無限期的等待,不能自動喚醒。
- java.lang.Object類方法。
notify()
- 喚醒一個正在等待當前對象的waiting線程,遵循FIFO(先進先出)策略。
- notify()調用後,並不是馬上就釋放對象鎖,而是在相應的synchronized{…}語句塊執行結束後,自動釋放鎖後,JVM在wait()對象鎖的線程中隨機選取一線程賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了在線間同步、喚醒的操作。
- java.lang.Object類方法。
notifyAll()
- 喚醒正在等待當前對象的所有等待線程。
- 默認的喚醒策略是:LIFO(後進先出)。
- java.lang.Object類方法。
wait()與notify()
- Obj.wait(),與Obj.notify()必須與synchronized(Obj)一起使用,也就是wait()是針對已經獲取了Obj鎖進行操作。
- 從語法角度來說就是Obj.wait(),Obj.notify()必須在、synchronized(Obj){…}語句塊內。
- 從功能上來說wait就是指在該線程獲取對象鎖後,主動釋放對象鎖,同時本線程休眠。notify()就是對對象鎖的喚醒操作。
wait()與sleep()
共同點
- 都可以指定線程阻塞的時間。
- 都可以通過interrupt()方法打斷(不建議使用)線程的暫停狀態 ,從而使線程立刻拋出InterruptedException。如果線程A希望立即結束線程B,則可以對線程B對應的Thread實例調用interrupt方法。如果此刻線程B正在wait/sleep /join,則線程B會立刻拋出InterruptedException,在catch() {} 中直接return即可安全地結束線程。
需要注意的是,InterruptedException是線程自己從內部拋出的,並不是interrupt()方法拋出的。對某一線程調用 interrupt()時,如果該線程正在執行普通的代碼,那麼該線程根本就不會拋出InterruptedException。但是,一旦該線程進入到 wait()/sleep()/join()後,就會立刻拋出InterruptedException 。
不同點
- sleep()是Thread類的方法,wait()是Object類的方法。
- sleep()方法沒有釋放鎖,而wait()方法釋放了鎖。
- wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep可以在任何地方使用。
執行notify()後,waiting線程可能的狀態
線程a先搶到了對象o的鎖,然後wait,然後b搶到了o的鎖,然後b中調用o.notify並釋放鎖,此時a是running狀態還是blocked狀態??
如果b在執行完notify()後沒有釋放鎖則線程a是阻塞等待,
如果線程b執行完同步代碼塊(釋放鎖)後,則線程a就是就緒態,不一定是運行態
代碼實操,三線程打印ABC
建立三個線程,A線程打印10次A,B線程打印10次B,C線程打印10次C,要求線程同時運行,交替打印10次ABC。這個問題用Object的wait(),notify()就可以很方便的解決。
public class T08WaitNotify {
class T implements Runnable{
// 自定義線程名稱
private String name;
// 前一個obj
private Object prev;
// 當前obj
private Object self;
T(String name, Object prev, Object self){
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 0;
while (count < 10) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count++;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
T08WaitNotify obj = new T08WaitNotify();
Object oa = new Object();
Object ob = new Object();
Object oc = new Object();
T a = obj.new T("A", oc, oa);
T b = obj.new T("B", oa, ob);
T c = obj.new T("C", ob, oc);
Thread ta = new Thread(a);
Thread tb = new Thread(b);
Thread tc = new Thread(c);
ta.start();
Thread.sleep(1000); // 確保按照A、B、C順序執行
tb.start();
Thread.sleep(1000);
tc.start();
Thread.sleep(1000);
}
}
代碼流程說明
A線程啓動,依次取得c、a鎖,打印A,執行notify()喚醒任意一個a對象等待池中的線程,待synchronized(self){}塊執行完後釋放a鎖,執行wait(),將A線程放入c對象相關的等待池中等待被喚醒,同時釋放c鎖; 1秒後,B線程啓動,依次取得a、b鎖,打印B,執行notify()喚醒任意一個b對象等待池中的線程,待synchronized(self){}塊執行完後釋放b鎖,執行wait(),將B線程放入a對象相關的等待池中等待被喚醒,同時釋放a鎖;1秒後,C線程啓動,依次取得b、c鎖,打印C,執行notify()喚醒任意一個c對象等待池中的線程(此時A線程被喚醒),待synchronized(self){}塊執行完後釋放c鎖,執行wait(),將C線程放入b對象相關的等待池中等待被喚醒,同時釋放b鎖。
至此,完成一次ABC的打印。繼續執行,從A線程開始,執行各自線程中的下一次循環。
特別說明
Java源碼中wait()默認調用的是wait(0),表明線程無限等待,只能被其他線程喚醒。
參考
https://blog.csdn.net/u010002184/article/details/82912225
https://blog.csdn.net/hj1997a/article/details/84284973