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,運行結果。

五,總結

​ 線程之間通信就做這么一個簡單的總結,以上內容如有錯誤,歡迎留言指正。

感謝閱讀!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM