wait()與notify()
一,前言
簡單畫了一下執行緒的流程圖,只是一個大概。如圖所示,執行緒有多種狀態,那麼不同狀態之間是如何切換的,下面主要總結關於wait()和notify()的使用。
二,wait()
wait()和notify()都是定義在Object類中,為什麼如此設計。因為synchronized中的這把鎖可以是任意物件,所以任意物件都可以呼叫wait()和notify(),並且只有同一把鎖才能對執行緒進行操作,不同鎖之間是不可以相互操作的,所以wait和notify屬於Object。請看如下API文件說明。
wait()提供三種構造方法,但前兩種最為常用,wait()是讓執行緒一直處於等待狀態,直到手動喚醒,而wait(long timeout)可以指定等待時間,之後會自動喚醒。
呼叫wait方法可以讓當前執行緒進入等待喚醒狀態,該執行緒會處於等待喚醒狀態直到另一個執行緒呼叫了object物件的notify方法或者notifyAll方法。
三,notify()
notify()喚醒等待的執行緒,如果監視器種只有一個等待執行緒,使用notify()可以喚醒。但是如果有多條執行緒notify()是隨機喚醒其中一條執行緒,與之對應的就是notifyAll()就是喚醒所有等待的執行緒,請看下面例項程式碼。
案例:定義兩條執行緒,分別讓其執行緒等待,及執行緒喚醒。
1,定義執行緒。
public class SetTarget implements Runnable{ private Demo demo; public SetTarget(Demo demo) { this.demo = demo; } @Override public void run() { demo.set(); } }
public class GetTarget implements Runnable {
private Demo demo;
public GetTarget(Demo demo) {
this.demo = demo;
}
@Override
public void run() {
demo.get();
}
}
2,編寫main方法。
public class Demo { // 定義一個訊號量 private volatile int signal; public static void main(String[] args) { Demo demo = new Demo(); SetTarget set = new SetTarget(demo); GetTarget get = new GetTarget(demo); // 開啟執行緒, new Thread(get).start(); new Thread(get).start(); new Thread(get).start(); new Thread(get).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(set).start(); } // set方法喚醒執行緒 public synchronized void set() { signal = 1; // notify方法會隨機叫醒一個處於wait狀態的執行緒 notify(); // notifyAll叫醒所有的處於wait執行緒,爭奪到時間片的執行緒只有一個 //notifyAll(); System.out.println("叫醒執行緒叫醒之後休眠開始..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } // get方法使執行緒進入等待狀態 public synchronized int get() { System.out.println(Thread.currentThread().getName() + " 方法執行了..."); if (signal != 1) { try { wait(); System.out.println("叫醒之後"); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + " 方法執行完畢..."); return signal; } }
3,執行結果。
分析:一共開啟了4個執行緒,當全部進入等待狀態時,呼叫notify()方法喚醒執行緒,但很明顯只喚醒了其中一條執行緒。右上角顯示程式並沒有停止,原因就是其他3條執行緒仍在處於等待狀態。
使用notifyAll()喚醒執行緒:
四,生產者-消費者模式
生產者-消費者模式,生產者生產商品,然後通知消費者進行消費。
1,定義生產者
public class Vendor {
// 定義庫存數量
private int count;
// 定義最大庫存
private final int MAX_COUNT = 10;
public synchronized void production() {
while (count >= MAX_COUNT) {
try {
System.out.println(Thread.currentThread().getName() + "庫存數量達到最大值,停止生產。");
// 此時生產執行緒全部進入等待狀態
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 否則生產商品
count++;
System.out.println(Thread.currentThread().getName() + "正在生產商品,當前庫存為:" + count);
notifyAll();
}
public synchronized void consumers() {
while (count <= 0) {
try {
System.out.println(Thread.currentThread().getName() + "沒有商品了,消費者處於等待狀態...");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println(Thread.currentThread().getName() + "正在消費,當前庫存為:" + count);
notifyAll();
}
}
2,分別定義兩條執行緒。
public class SetTarget implements Runnable {
private Vendor vendor;
public SetTarget(Vendor vendor) {
this.vendor = vendor;
}
@Override
public void run() {
while(true){
vendor.production();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class GetTarget implements Runnable {
private Vendor vendor;
public GetTarget(Vendor vendor) {
this.vendor = vendor;
}
@Override
public void run() {
while(true){
vendor.consumers();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
3,主方法。
public class Demo {
public static void main(String[] args) {
Vendor vendor = new Vendor();
SetTarget set = new SetTarget(vendor);
GetTarget get = new GetTarget(vendor);
// 開啟執行緒生產商品
new Thread(set).start();
new Thread(set).start();
new Thread(set).start();
new Thread(set).start();
// 開啟消費者執行緒
new Thread(get).start();
}
}
4,執行結果。
五,總結
執行緒之間通訊就做這麼一個簡單的總結,以上內容如有錯誤,歡迎留言指正。
感謝閱讀!