簡單介紹
wait()方法是Object類裡的方法;當一個執行緒執行到wait()方法時,它就進入到一個和該物件相關的等待池中,同時失去(釋放)了物件的機鎖(暫時失去機鎖,wait(long timeout)超時時間到後還需要返還物件鎖);其他執行緒可以訪問;wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的執行緒。wiat()必須放在synchronized block中,否則會在program runtime時扔 出“java.lang.IllegalMonitorStateException”異常。
簡單的介紹就到這裡,現在我們用一個例子來深入理解一下
package com.example.demo.test.MultithreadingTest;
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
int x=0;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
x++;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (prev){
System.out.print(x);
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();
Thread.sleep(100);
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
輸出結果:
ABC1A1B1C2A2B2C3A3B3C4A4B4C5A5B5C6A6B6C7A7B7C8A8B8C9A9B9C10
這段程式碼為三執行緒間的同步喚醒操作,主要的目的就是ThreadA->ThreadB->ThreadC->ThreadA迴圈執行三個執行緒。
為了控制執行緒執行的順序,那麼就必須要確定喚醒、等待的順序,所以每一個執行緒必須同時持有兩個物件鎖,才能繼續執行。一個物件鎖是prev,就是前一個執行緒所持有的物件鎖。還有一個就是自身物件鎖。主要的思想就是,為了控制執行的順序,必須要先持有prev鎖,也就前一個執行緒要釋放自身物件鎖,再去申請自身物件鎖,兩者兼備時列印,之後首先呼叫self.notify()釋放自身物件鎖,喚醒下一個等待執行緒,再呼叫prev.wait()釋放prev物件鎖,終止當前執行緒,等待迴圈結束後再次被喚醒。prev.wait()之後,加入了一個物件鎖是prev的輸出x,用來展示什麼時候釋放了prev物件鎖。
執行上述程式碼,可以發現先列印出A,再釋放A,然後C鎖,進入ThreadB,列印出B,再釋放B,然後A鎖,進入ThreadC,列印出C,再釋放C,然後B鎖,此時會進入ThreadA,繼續執行開始時wait的執行緒,輸出x,列印出A,再釋放A,然後C鎖……如此迴圈