1. 程式人生 > >多執行緒的執行緒間通訊

多執行緒的執行緒間通訊

在這裡插入圖片描述

一、兩個執行緒間的通訊

  • 1.什麼時候需要通訊
    • 多個執行緒併發執行時, 在預設情況下CPU是隨機切換執行緒的
    • 如果我們希望他們有規律的執行, 就可以使用通訊, 例如每個執行緒執行一次列印
  • 2.怎麼通訊
    • 如果希望執行緒等待, 就呼叫wait()
    • 如果希望喚醒等待的執行緒, 就呼叫notify();
    • 這兩個方法必須在同步程式碼中執行, 並且使用同步鎖物件來呼叫
public class Demo1_Notify {

	/**
	 * @param args
	 * 等待喚醒機制
	 */
public static void main(String[] args) { final Printer p = new Printer(); 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(); } } //等待喚醒機制 class Printer { private int flag = 1; public void print1() throws InterruptedException { synchronized(this) {
if(flag != 1) { this.wait(); //當前執行緒等待 } System.out.print("黑"); System.out.print("馬"); System.out.print("程"); System.out.print("序"); System.out.print("員"); System.out.print("\r\n"); flag = 2; this.notify(); //隨機喚醒單個等待的執行緒 } } public void print2() throws InterruptedException { synchronized(this) { if(flag != 2) { this.wait(); } System.out.print("傳"); System.out.print("智"); System.out.print("播"); System.out.print("客"); System.out.print("\r\n"); flag = 1; this.notify(); } } }

二、三個或三個以上執行緒通訊

  • 如果多個執行緒之間通訊, 需要使用notifyAll()通知所有執行緒, 用while來反覆判斷條件
public class Demo2_NotifyAll {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		final 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();
	}

}
/*1,在同步程式碼塊中,用哪個物件鎖,就用哪個物件呼叫wait方法
 * 2,為什麼wait方法和notify方法定義在Object這類中?
 * 	因為鎖物件可以是任意物件,Object是所有的類的基類,所以wait方法和notify方法需要定義在Object這個類中
 * 3,sleep方法和wait方法的區別?
 * a,sleep方法必須傳入引數,引數就是時間,時間到了自動醒來
 *   wait方法可以傳入引數也可以不傳入引數,傳入引數就是在引數的時間結束後等待,不傳入引數就是直接等待
 * b,sleep方法在同步函式或同步程式碼塊中,不釋放鎖,睡著了也抱著鎖睡
 * 	wait方法在同步函式或者同步程式碼塊中,釋放鎖
 */ 
class Printer2 {
	private int flag = 1;
	public void print1() throws InterruptedException {							
		synchronized(this) {
			while(flag != 1) {
				this.wait();					//當前執行緒等待
			}
			System.out.print("黑");
			System.out.print("馬");
			System.out.print("程");
			System.out.print("序");
			System.out.print("員");
			System.out.print("\r\n");
			flag = 2;
			//this.notify();						//隨機喚醒單個等待的執行緒
			this.notifyAll();
		}
	}
	
	public void print2() throws InterruptedException {
		synchronized(this) {
			while(flag != 2) {
				this.wait();					//執行緒2在此等待
			}
			System.out.print("傳");
			System.out.print("智");
			System.out.print("播");
			System.out.print("客");
			System.out.print("\r\n");
			flag = 3;
			//this.notify();
			this.notifyAll();
		}
	}
	
	public void print3() throws InterruptedException {
		synchronized(this) {
			while(flag != 3) {
				this.wait();						//執行緒3在此等待,if語句是在哪裡等待,就在哪裡起來
													//while迴圈是迴圈判斷,每次都會判斷標記
			}
			System.out.print("i");
			System.out.print("t");
			System.out.print("h");
			System.out.print("e");
			System.out.print("i");
			System.out.print("m");
			System.out.print("a");
			System.out.print("\r\n");
			flag = 1;
			//this.notify();
			this.notifyAll();
		}
	}
}

三、互斥鎖

  • 1.同步
    • 使用ReentrantLock類的lock()和unlock()方法進行同步
  • 2.通訊
    • 使用ReentrantLock類的newCondition()方法可以獲取Condition物件
    • 需要等待的時候使用Condition的await()方法, 喚醒的時候用signal()方法
    • 不同的執行緒使用不同的Condition, 這樣就能區分喚醒的時候找哪個執行緒了
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Demo3_ReentrantLock {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		final Printer3 p = new Printer3();
		
		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 Printer3 {
	private ReentrantLock r = new ReentrantLock();
	private Condition c1 = r.newCondition();
	private Condition c2 = r.newCondition();
	private Condition c3 = r.newCondition();
	
	private int flag = 1;
	public void print1() throws InterruptedException {							
		r.lock();								//獲取鎖
			if(flag != 1) {
				c1.await();
			}
			System.out.print("黑");
			System.out.print("馬");
			System.out.print("程");
			System.out.print("序");
			System.out.print("員");
			System.out.print("\r\n");
			flag = 2;
			//this.notify();						//隨機喚醒單個等待的執行緒
			c2.signal();
		r.unlock();								//釋放鎖
	}
	
	public void print2() throws InterruptedException {
		r.lock();
			if(flag != 2) {
				c2.await();
			}
			System.out.print("傳");
			System.out.print("智");
			System.out.print("播");
			System.out.print("客");
			System.out.print("\r\n");
			flag = 3;
			//this.notify();
			c3.signal();
		r.unlock();
	}
	
	public void print3() throws InterruptedException {
		r.lock();
			if(flag != 3) {
				c3.await();
			}
			System.out.print("i");
			System.out.print("t");
			System.out.print("h");
			System.out.print("e");
			System.out.print("i");
			System.out.print("m");
			System.out.print("a");
			System.out.print("\r\n");
			flag = 1;
			c1.signal();
		r.unlock();
	}
}