執行緒間通訊_等待/通知機制

z1340954953發表於2018-04-23

等待/通知的相關方法式任意java物件都具備的,notify,wait方法被定義在java.lang.Object,都是final,不可重寫



,是例項方法,新手比較容易出錯的地方是,呼叫時候必須呼叫 鎖物件.wait()/鎖物件.nofity(),並且只能在臨界區中呼叫

具體瞭解下,notify,wait定義

等待/通知方法
名稱描述
notify()通知在同一個物件上等待的執行緒,使其從wait()方法返回,而返回的前提是該執行緒獲取到物件的鎖
notifyAll()通知所有等待同一個物件鎖的執行緒
wait()呼叫該方法的執行緒進入WAITING狀態,並且釋放物件的鎖,從方法描述上可以看出是響應中斷的/或者接收到notify才會返回
wait(long)超時等待一段時間,沒有接受到通知就超時返回
wait(long,int)對於超時時間更細粒度的控制,可以達到納秒

等待/通知機制,舉個例子來說執行緒A呼叫物件obj的wait方法進入WAITING狀態,另一個執行緒呼叫同一個物件的notify方法喚醒執行緒A,執行緒A繼續執行wait後的程式

簡單的案例 ,單個生產者、單個消費者

鎖物件:

package com.ftf.thread.test;

public class Student {
	public volatile boolean flag;
	public Student(boolean flag) {
		this.flag = flag;
	}
}

生產者:

package com.ftf.thread.test;

public class Product {
	private Student obj;

	public Product(Student obj) {
		this.obj = obj;
	}

	public void dothis() {
		synchronized (obj) {
			try{
				if(!obj.flag) {
					obj.wait();
				} 
					System.out.println("我是生產者執行緒"
							+ Thread.currentThread().getName());
					Thread.sleep(1000);
					obj.flag = false;
					obj.notify();
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			
	}
}

}

消費者執行緒:

package com.ftf.thread.test;

public class Customer {
	private Student obj;

	public Customer(Student obj) {
		this.obj = obj;
	}

	public void dothis() {
		synchronized (obj) {
			try {
				if(obj.flag) {
					obj.wait();
				} 
				System.out.println("我是消費者執行緒"
						+ Thread.currentThread().getName());
				Thread.sleep(1000);
				obj.flag = true;
				obj.notify();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

main方法:

package com.ftf.thread.test;

public class NotifyWaitDemo {
	public static void main(String[] args) {
		Student obj = new Student(true);
		Product notify = new Product(obj);
		Customer wait = new Customer(obj);
		Thread1 t1 = new Thread1(notify);
		Thread2 t4 = new Thread2(wait);
		t1.start();
		t4.start();
	}
}
class Thread1 extends Thread{
	private Product notify;
	public Thread1(Product notify){
		this.notify= notify;
	}
	@Override
	public void run() {
		while(true){
			notify.dothis();
		}
	}
}
class Thread2 extends Thread{
	private Customer wait;
	public Thread2(Customer wait){
		this.wait = wait;
	}
	@Override
	public void run() {
		while(true){
			wait.dothis();
		}
	}
}

說明:

1.通過類Student flag定義為volatile這樣,通過記憶體可見性,兩個執行緒修改這個值,另一個執行緒都能看到,來控制兩個執行緒釋放鎖和獲取鎖互動

2. 在單個消費者, 單個生產者和使用notify的情況下,使用if判斷沒有問題

多個生產者,多個消費者需要使用while作為條件判斷

驗證在多生產者消費者情況下,使用if存在的問題

生產者dothis方法修改:  while->if,因為是多個生產者消費者,需要將notify->notifyAll

public void dothis() {
		synchronized (obj) {
			try{
				if (!obj.flag) {
					obj.wait();
				} 
					System.out.println("我是生產者執行緒"
							+ Thread.currentThread().getName());
					Thread.sleep(1000);
					obj.flag = false;
					obj.notifyAll();
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			
	}

同樣,修改消費者程式碼

public void dothis() {
		synchronized (obj) {
			try {
				if (obj.flag) {
					obj.wait();
				} 
				System.out.println("我是消費者執行緒"
						+ Thread.currentThread().getName());
				Thread.sleep(1000);
				obj.flag = true;
				obj.notifyAll();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

,修改main方法:

public static void main(String[] args) {
		Student obj = new Student(true);
		Product notify = new Product(obj);
		Customer wait = new Customer(obj);
		Thread1 t1 = new Thread1(notify);
		Thread1 t2 = new Thread1(notify);
		Thread2 t3 = new Thread2(wait);
		Thread2 t4 = new Thread2(wait);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

測試結果:


出現了,消費者執行緒連續消費兩次的情況。

說明:

在多生產者,多消費者情況下,對於wait方法的判斷使用while條件判斷,而不是使用if,

是為了防止在多生產者,多消費者的情況下,如果使用if的條件,當前執行緒釋放鎖後,再次獲取到鎖後,

不進行檢查flag,直接往下執行,那本例來說,就是消費者執行緒釋放掉鎖後,再次獲取到鎖,再次消費




相關文章