1. 程式人生 > >Java多執行緒-52-三個和三個以上執行緒之間的通訊

Java多執行緒-52-三個和三個以上執行緒之間的通訊

        前面一篇介紹了兩個執行緒之間的通訊,那麼三個執行緒和三個以上執行緒之間的通訊是如何實現呢。我們前面一篇在查詢Object類的時候,知道有一個wait()和notify()方法,同時還有一個notifyAll()方法。這個notfiyAll()方法就是來解決三個以上執行緒通訊的。

1.基於前面知識,新增一個執行緒,看看執行效果

package thread;

public class NotifyAll_Demo {

	public static void main(String[] args) {
		Printer2 p = new Printer2();
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print1();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print2();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print3();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
	}

}


class Printer2 {
	private int flag = 1;
	public void print1() throws InterruptedException {
		synchronized (this) {
			if(flag != 1) {
				this.wait();  // 設定執行緒等待,如果flag 不等於1
			}
			System.out.print("跟");
			System.out.print("我");
			System.out.print("一");
			System.out.print("起");
			System.out.println("念");
			flag = 2;
			this.notify(); // 設定flag等於2,使用執行緒喚醒功能,其他執行緒就可以啟動
		}
	}
	
	public void print2() throws InterruptedException {
		synchronized (this) {
			if(flag != 2) {
				this.wait();  // 設定執行緒等待,如果flag 不等於2
			}
			System.out.print("做");
			System.out.print("測");
			System.out.print("試");
			System.out.print("死");
			System.out.print("路");
			System.out.print("一");
			System.out.println("條");
			flag = 3;
			this.notify();  //隨機喚醒單個等待的執行緒
		}
	}
	
	public void print3() throws InterruptedException {
		synchronized (this) {
			if(flag != 3) {
				this.wait();  // 設定執行緒等待,如果flag 不等於2
			}
			System.out.print("信");
			System.out.print("才");
			System.out.print("怪");
			System.out.println("呢");
			flag = 1;
			this.notify();  //隨機喚醒單個等待的執行緒
		}
	}
}

執行效果:

做測試死路一條
跟我一起念
信才怪呢
做測試死路一條
跟我一起念
信才怪呢
做測試死路一條
跟我一起念
信才怪呢
做測試死路一條
跟我一起念
信才怪呢
做測試死路一條
跟我一起念

發現列印順序有問題,主要有兩個原因,if語句有一個特點,程式碼在哪裡停止(上面wait)就在哪裡起來。第二個特點就是notify()是隨機喚醒等待執行緒。這個隨機特點就造成了列印結果順序不可控。

2.if語句換成while語句,notify()方法換成notifyAll()方法

if沒有每次都對flag進行判斷,但是while語句會每次都進行條件判斷。notifyAll()就是把全部的執行緒都喚醒,然後這些執行緒去判斷,再執行相關列印。

package thread;

public class NotifyAll_Demo {

	public static void main(String[] args) {
		Printer2 p = new Printer2();
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print1();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print2();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print3();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
	}

}


class Printer2 {
	private int flag = 1;
	public void print1() throws InterruptedException {
		synchronized (this) {
			while(flag != 1) {
				this.wait();  // 設定執行緒等待,如果flag 不等於1
			}
			System.out.print("跟");
			System.out.print("我");
			System.out.print("一");
			System.out.print("起");
			System.out.println("念");
			flag = 2;
			this.notifyAll(); 
		}
	}
	
	public void print2() throws InterruptedException {
		synchronized (this) {
			while(flag != 2) {
				this.wait();  // 設定執行緒等待,如果flag 不等於2
			}
			System.out.print("做");
			System.out.print("測");
			System.out.print("試");
			System.out.print("死");
			System.out.print("路");
			System.out.print("一");
			System.out.println("條");
			flag = 3;
			this.notifyAll();  
		}
	}
	
	public void print3() throws InterruptedException {
		synchronized (this) {
			while(flag != 3) {
				this.wait();  // 設定執行緒等待,如果flag 不等於2
			}
			System.out.print("信");
			System.out.print("才");
			System.out.print("怪");
			System.out.println("呢");
			flag = 1;
			this.notifyAll();  
		}
	}
}

執行效果:

跟我一起念
做測試死路一條
信才怪呢
跟我一起念
做測試死路一條
信才怪呢
跟我一起念
做測試死路一條
信才怪呢
跟我一起念
做測試死路一條
信才怪呢
跟我一起念
做測試死路一條
信才怪呢
...

 

多執行緒之間需要注意的問題

1.在同步程式碼塊中,用哪個物件鎖,就用哪個物件呼叫wait方法(例如上面的this)
2.為什麼wait方法和notify方法定義在Object這個類中
   因為鎖物件可以是任意物件,Object是所有類的基類,所以wait方法和notify方法需要定義在Object類中

3.sleep方法和wait方法的區別
第一個區別:sleep方法必須傳入引數,引數就是時間,時間到了自動醒來。wait方法可以傳入引數也可以不傳入引數,傳入引數就是在引數時間結束後等待,不傳入引數就是直接等待。
第二個區別:sleep方法在同步函式或者同步程式碼塊中,不釋放鎖。wait方法在同步函式或者同步程式碼塊中,釋放鎖。