1. 程式人生 > >Java多執行緒學習---Condition和wait、notify(十三)

Java多執行緒學習---Condition和wait、notify(十三)

1.問題:實現兩個執行緒交叉執行(Condition和wait、notify都可以實現)

public class ConditionStudy {
	
	
	public static void main(String[] args) {
		//執行緒程式碼
		BussinessTest b = new BussinessTest();
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 1;i<=50;i++){
					b.subThreadCreate(i);
				}
			}
		}).start();
		
		for(int i = 1;i<=50;i++){
			b.mainThreadCreate(i);
		}
		
	}
	
}
//資源類
class BussinessTest{
	//指示是否該子執行緒執行的布林臨時變數
	private boolean isSub = true;
	
	private Lock lock = new ReentrantLock();
	
	private Condition condition = lock.newCondition();
	//子執行緒資源方法
	public  void subThreadCreate(int i){
		lock.lock();
		try{
			//如果不該子執行緒執行,子執行緒等待
			while(!isSub){
				try {
					condition.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			for(int j = 1;j<=10;j++){
				System.out.println("sub thread number of "+j+" loop of "+i);
			}
			//子執行緒執行完,讓指示是否該子執行緒執行為false
			isSub = false;
			//喚醒等待的主程序
			condition.signal();
		}finally{
			lock.unlock();
		}
	}
	//主執行緒資源方法
	public  void mainThreadCreate(int i){
		lock.lock();
		try{
			//如果該子程序去執行,讓主程序等待
				while(isSub){
					try {
						condition.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				for(int j = 1;j<=100;j++){
					System.out.println("MAIN thread number of "+j+" loop of "+i);
				}
				//主程序執行完,輪到子程序去執行,令isSub為true
				isSub = true;
				//喚醒等待的子程序
				condition.signal();
		}finally{
			lock.unlock();
		}
	}
}

2.為什麼有Object的wait方法和notify方法還需要Condition呢?

synchronized 方式對應的 wait, notify 不能有多個謂詞條件,Lock 對應的 Condition await, signal 則可以有多個謂詞條件

沒有多個謂詞條件帶來的問題在於

例如佇列已滿,所有的生產者現場阻塞,某個時刻消費者消費了一個元素,則需要喚醒某個生產者執行緒,而通過 Object notify 方式喚醒的執行緒不能確保一定就是一個生產者執行緒,因為 notify 是隨機喚醒某一個正在該 synchronized 對應的鎖上面通過 wait 方式阻塞的執行緒,如果這時正好還有消費者執行緒也在阻塞中,則很可能喚醒的是一個消費者執行緒;signalAll 更是會喚醒所有在對應鎖上通過 wait 方式阻塞的執行緒,而不管是生產者還是消費者執行緒。

與之不同的 Condition await, signal 方式則可以對應多個謂詞條件(notEmpty, notFull),可以很方便的實現讓生產者執行緒和消費者執行緒分別在不同的謂詞條件上進行等待。所有的生產者執行緒在 notEmpty 謂詞條件上等待,所有的消費者執行緒在 notFull 謂詞條件上等待,當佇列是滿的時候所有的生產者執行緒阻塞,新增元素之後則喚醒某個消費者執行緒,此時則不用擔心會喚醒消費者執行緒。

3.三個執行緒通訊交叉執行---條件:執行緒一執行完,讓執行緒二執行,執行緒二執行完讓執行緒三執行,執行緒三執行完,再讓執行緒一執行,一次次迴圈執行(只能用Condition來實現)

程式碼示例

public class ConditionStudy {
	
	
	public static void main(String[] args) {
		BussinessTest b = new BussinessTest();
		//執行緒1 執行5次
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 1;i<=5;i++){
					b.subThread1(i);
				}
			}
		}).start();
		//執行緒2 執行10次
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 1;i<=10;i++){
					b.subThread2(i);
				}
			}
		}).start();
		//執行緒3 執行15次
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 1;i<=15;i++){
					b.subThread3(i);
				}
			}
		}).start();
	}
	
}
//資源類
class BussinessTest{
	//指示該哪個執行緒執行
	private int shouldSub = 1;
	
	private Lock lock = new ReentrantLock();
	
	private Condition condition1 = lock.newCondition();
	private Condition condition2 = lock.newCondition();
	private Condition condition3 = lock.newCondition();
	//執行緒1執行方法
	public  void subThread1(int i){
		lock.lock();
		try{
			//如果不該1執行緒,讓它等待
			while(shouldSub != 1){
				try {
					condition1.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			for(int j = 1;j<=10;j++){
				System.out.println("sub thread1 number of "+j+" loop of "+i);
			}
			//1執行緒執行完了,讓2去執行
			shouldSub  = 2;
			//喚醒等待的2執行緒
			condition2.signal();
		}finally{
			lock.unlock();
		}
	}
	//執行緒1執行方法
		public  void subThread2(int i){
			lock.lock();
			try{
				//如果不該2執行緒,讓它等待
				while(shouldSub != 2){
					try {
						condition2.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				for(int j = 1;j<=10;j++){
					System.out.println("sub thread2 number of "+j+" loop of "+i);
				}
				//2執行緒執行完了,讓3去執行
				shouldSub  = 3;
				//喚醒等待的3執行緒
				condition3.signal();
			}finally{
				lock.unlock();
			}
		}
		//執行緒1執行方法
		public  void subThread3(int i){
			lock.lock();
			try{
				//如果不該3執行緒,讓它等待
				while(shouldSub != 3){
					try {
						condition3.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				for(int j = 1;j<=10;j++){
					System.out.println("sub thread3 number of "+j+" loop of "+i);
				}
				//3執行緒執行完了,讓1去執行
				shouldSub  = 1;
				//喚醒等待的1執行緒
				condition1.signal();
			}finally{
				lock.unlock();
			}
		}

}