1. 程式人生 > >執行緒之間的通訊(wait()/notify()機制)——等待喚醒機制

執行緒之間的通訊(wait()/notify()機制)——等待喚醒機制

wait() : 使當前同步監視器上的執行緒進入等待狀態, 同時釋放鎖     物件名.wait()
notify() / notifyAll() : 喚醒當前同步監視器上等待狀態的一個(所有)執行緒    物件名.notify()
-------------------- 上述方法必須使用在同步方法中(必須在加鎖的狀態)

wait(),notify();notifyAll()這些方法定義在object類原因是瑣是任意物件,而這幾個方法又必須用在鎖當中

例1:使用兩個執行緒,交替列印1-100

public class PrintRunnable implements Runnable {

	int i = 0;

	@Override
	public synchronized void run() {
		while (true) {
			notify();//喚醒等待的執行緒
			if (i < 100) {
				System.out.println(Thread.currentThread().getName() + ":" + i++);
			}
			try {
				wait();//等待 同時釋放鎖
			} catch (InterruptedException e) {
			}
		}
	}
}
public class MyTest {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//執行緒執行體 下面的執行緒傳入同一個runnable
		PrintRunnable printRunnable = new PrintRunnable();

		//兩個執行緒執行列印
		Thread print1 = new Thread(printRunnable, "列印者1");
		Thread print2 = new Thread(printRunnable, "列印者2");

		//開始列印
		print1.start();
		print2.start();
	}
}

例2:模擬售票程式,實現三個視窗同時賣100張票

public class Tickets implements Runnable {

	int tick = 100;
	Object object = new Object();

	@Override
	public void run() {
		while (true) {
			synchronized (object) {
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//將操作共享變數的程式碼塊放入同步監視器裡
				if (tick > 0) {
					tick--;
					System.out.println(Thread.currentThread().getName() + "完成售票,餘票為 :" + tick);
				}
			}
		}
	}
}
public class MyTest {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//執行緒執行體 下面的幾個執行緒 構造器傳入同一個runnable 所以共享變數不需要static修飾也可以
		Tickets tickets = new Tickets();

		//三個視窗 三個執行緒
		Thread win1 = new Thread(tickets, "視窗1");
		Thread win2 = new Thread(tickets, "視窗2");
		Thread win3 = new Thread(tickets, "視窗3");

		//開始賣票
		win1.start();
		win2.start();
		win3.start();

	}
}

例3:生產者消費者問題

店員擁有產品屬性

public class Clerk {
	//屬性 店員的產品
	private int product;

	// 公共的get/set方法
	public int getProduct() {
		return product;
	}

	public void setProduct(int product) {
		this.product = product;
	}

	// 無參和全參構造器
	public Clerk() {}

	public Clerk(int product) {
		this.product = product;
	}

	// 生產商送貨的方法
	public synchronized void getproducts() {
		if (product >= 20) {
			System.out.println("貨物已滿");
			try {
				wait();
			} catch (InterruptedException e) {
			}
		} else {
			System.out.println(Thread.currentThread().getName() + "成功送來第" + ++product + "號貨物");
			notifyAll();
		}
	}

	// 消費者取走貨物的方法
	public synchronized void takeOffproduct() {
		if (product <= 0) {
			System.out.println("已缺貨!!!");
			try {
				wait();
			} catch (InterruptedException e) {
			}
		} else {
			System.out.println(Thread.currentThread().getName() + "成功取走第" + product-- + "號貨物");
			notifyAll();
		}
	}
}

生產者

public class Productor implements Runnable{
	private Clerk clerk;

	// 公共的get/set方法
	public Clerk getClerk() {
		return clerk;
	}

	public void setClerk(Clerk clerk) {
		this.clerk = clerk;
	}

	// 無參和全參構造器
	public Productor() {
	}

	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}

	//實現介面的run方法執行體
	@Override
	public void run() {
		while (true) {
			clerk.getproducts();
		}
	}
}

消費者

public class Customer implements Runnable{
	private Clerk clerk;

	// 公共的get/set方法
	public Clerk getClerk() {
		return clerk;
	}

	public void setClerk(Clerk clerk) {
		this.clerk = clerk;
	}

	// 無參和全參構造器
	public Customer() {
	}

	public Customer(Clerk clerk) {
		this.clerk = clerk;
	}

	// 消費者取走貨物的方法,實現介面run方法執行體
	@Override
	public void run() {
		while (true) {
			clerk.takeOffproduct();
		}
	}
}

test

public class MyTest {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//店員 擁有產品屬性
		Clerk clerk = new Clerk();
		//生產者 生產執行體
		Productor productor = new Productor(clerk);
		//消費者消費執行體
		Customer customer = new Customer(clerk);
		//生產產品的執行緒
		Thread pro = new Thread(productor, "生產者");
		//消費產品的執行緒
		Thread cus = new Thread(customer, "消費者");
		pro.start();//生產產品開始
		cus.start();//消費產品開始
	}
}

測試結果

消費者成功取走第20號貨物
消費者成功取走第19號貨物
消費者成功取走第18號貨物
消費者成功取走第17號貨物
消費者成功取走第16號貨物
消費者成功取走第15號貨物
消費者成功取走第14號貨物
消費者成功取走第13號貨物
消費者成功取走第12號貨物
消費者成功取走第11號貨物
消費者成功取走第10號貨物
消費者成功取走第9號貨物
消費者成功取走第8號貨物
消費者成功取走第7號貨物
消費者成功取走第6號貨物
消費者成功取走第5號貨物
消費者成功取走第4號貨物
消費者成功取走第3號貨物
消費者成功取走第2號貨物
消費者成功取走第1號貨物
已缺貨!!!
生產者成功送來第1號貨物
生產者成功送來第2號貨物
生產者成功送來第3號貨物
生產者成功送來第4號貨物
生產者成功送來第5號貨物
生產者成功送來第6號貨物
生產者成功送來第7號貨物
生產者成功送來第8號貨物
生產者成功送來第9號貨物
生產者成功送來第10號貨物
生產者成功送來第11號貨物
生產者成功送來第12號貨物
生產者成功送來第13號貨物
生產者成功送來第14號貨物
生產者成功送來第15號貨物
生產者成功送來第16號貨物
生產者成功送來第17號貨物
生產者成功送來第18號貨物
生產者成功送來第19號貨物
生產者成功送來第20號貨物
貨物已滿
消費者成功取走第20號貨物
消費者成功取走第19號貨物
消費者成功取走第18號貨物
消費者成功取走第17號貨物
消費者成功取走第16號貨物
消費者成功取走第15號貨物
消費者成功取走第14號貨物
消費者成功取走第13號貨物
消費者成功取走第12號貨物
消費者成功取走第11號貨物
消費者成功取走第10號貨物
消費者成功取走第9號貨物
消費者成功取走第8號貨物
消費者成功取走第7號貨物
消費者成功取走第6號貨物
消費者成功取走第5號貨物
消費者成功取走第4號貨物
消費者成功取走第3號貨物
消費者成功取走第2號貨物
消費者成功取走第1號貨物
已缺貨!!!
生產者成功送來第1號貨物
生產者成功送來第2號貨物
生產者成功送來第3號貨物
生產者成功送來第4號貨物
生產者成功送來第5號貨物
生產者成功送來第6號貨物
生產者成功送來第7號貨物
生產者成功送來第8號貨物
生產者成功送來第9號貨物
生產者成功送來第10號貨物
生產者成功送來第11號貨物
生產者成功送來第12號貨物
生產者成功送來第13號貨物
生產者成功送來第14號貨物
生產者成功送來第15號貨物
生產者成功送來第16號貨物
生產者成功送來第17號貨物
生產者成功送來第18號貨物
生產者成功送來第19號貨物
生產者成功送來第20號貨物
貨物已滿

例4:現在兩個執行緒,可以操作同一個變數,實現一個執行緒對該變數加1,一個執行緒對該變數減1, 實現交替,來十輪,變數初始值為0。

1 高內聚低耦合2 執行緒  操作  資源
public class ShareData {
	private int num = 0;

	// 增加資料的方法
	public synchronized void incrment() throws InterruptedException {
		// 此處應該用While不能用if:因為多個執行緒被喚醒進入if時會出錯誤,會出現虛假喚醒,while可以拉回重新判斷
		while (num != 0) {
			// 等待
			this.wait();
		}
		num++;
		System.out.println(Thread.currentThread().getName() + "\t" + num);
		// 喚醒其他所有執行緒
		this.notifyAll();
	}

	// 減少資料的方法
	public synchronized void descment() throws InterruptedException {
		while (num == 0) {
			// 等待
			this.wait();
		}
		num--;
		System.out.println(Thread.currentThread().getName() + "\t" + num);
		this.notifyAll();
	}
}
public class MyTest {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// 內部類如果要引用外部類的變數,則該變數必須為final,這是規定
		final ShareData sd = new ShareData();

		//執行緒1
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						sd.incrment();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "AA").start();

		//執行緒2
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						sd.descment();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "BB").start();

		//執行緒3
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						sd.incrment();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "CC").start();

		//執行緒4
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						sd.descment();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "DD").start();
	}
}