java5條件阻塞Condition的應用

1、概述

    一、在等待 Condition 時,容許發生「虛假喚醒」,這一般做爲對基礎平臺語義的讓步。對於大多數應用程序,這帶來的實際影響很小,由於 Condition 應該老是在一個循環中被等待,並測試正被等待的狀態聲明。某個實現能夠隨意移除可能的虛假喚醒,但建議應用程序程序員老是假定這些虛假喚醒可能發生,所以老是在一個循環中等待。java

    二、一個鎖內部能夠有多個Condition,即有多路等待和通知,能夠參看jdk1.5提供的Lock與Condition實現的可阻塞隊列的應用案例,從中除了要體味算法,還要體味面向對象的封裝。在傳統的線程機制中一個監視器對象上只能有一路等待和通知,要想實現多路等待和通知,必須嵌套使用多個同步監視器對象。(若是隻用一個Condition,兩個放的都在等,一旦一個放的進去了,那麼它通知可能會致使另外一個放接着往下走。程序員

2、代碼描述

    一、ConditionCommunication.java

將傳統wait()和notify() 改寫成Condition方式,須要Lock配合使用。算法

/** 
* @Title: TraditionalThreadCommunication.java 
* @Package com.lh.threadtest.t4 
* @Description: TODO
* @author Liu 
* @date 2018年1月15日 下午9:28:23 
* @version V1.0 
*/
package com.lh.threadtest.t10;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** 
* @ClassName: TraditionalThreadCommunication 
* @Description: 傳統線程同步通訊技術
* 
* 子線程循環10次,接着主線程循環100次,接着又回到子線程循環10次,接着再回到主線程
* 又循環100次,如此循環50次,請寫出程序。
* 
* 注意:
* 	Lock比傳統線程模型中的synchronized方式更加面向對象,與生活中的鎖相似,鎖自己也應該是一個對象。
* 兩個線程執行的代碼片斷要實現同步互斥的效果,它們必須用同一個Lock對象。鎖是上在表明要操做的資源的類的內部方法中,
* 而不是線程代碼中!
* 
* 總結:
* 	須要用到共同數據(包括同步鎖)或共同算法的若干個方法應該歸在同一個類中,這種設計正好體現了
* 高類聚和程序的健壯性。
* 
* @author Liu
* @date 2018年1月15日 下午9:28:23 
*  
*/
public class ConditionCommunication {
	public static void main(String[] args) {
		final Business business = new Business();
		
		new Thread(new Runnable() {
			public void run() {
				for(int i = 1; i <= 50; i++){
					try {
						business.sub(i);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		for(int i = 1; i <= 50; i++){
			try {
				business.main(i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
//不一樣點:	
//condition.business
//business
	
	//將具備相似功能的業務代碼抽象到同一個類中
	//下面兩個方法(sub/main)是互斥的,均是獨立的一個同步模塊,處於同一個互斥組(經過synchronized代表均屬於同一個類,同一時刻只能有一個線程持有該鎖)
	static class Business{
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		
		//主線程和子線程切換執行的一個標識
		boolean flag = true;
		
		void sub(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(!flag){
				//循環和if功能相仿,但更加完備
				while(!flag){
					//使當前線程等待...
//					this.wait();
					condition.await();
				}
				
				for(int j = 1; j <= 10; j++){
					System.out.println("sub thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = false;
				
				//喚醒其它正在等待的線程
//				this.notify();
				condition.signal();
			} finally {
				lock.unlock();
			}
		}
		
		void main(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(flag){
				//循環和if功能相仿,但更加完備
				while(flag){
					//使當前線程等待...
//					this.wait();
					condition.await();
				}
				
				for(int j = 1; j <= 100; j++){
					System.out.println("main thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = true;
				
				//喚醒其它正在等待的線程
//				this.notify();
				condition.signal();
			} finally {
				lock.unlock();
			}
		}
	}
}

    二、ThreeConditionCommunication.java

老大循環100次,而後老二循環10次,接着老三循環20次,又接着老大循環100次,老二又循環10次,老三又循環20次, 如此循環50次,請寫出程序。oop

/** 
* @Title: TraditionalThreadCommunication.java 
* @Package com.lh.threadtest.t4 
* @Description: TODO
* @author Liu 
* @date 2018年1月15日 下午9:28:23 
* @version V1.0 
*/
package com.lh.threadtest.t10;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** 
* @ClassName: TraditionalThreadCommunication 
* @Description: java5條件阻塞Condition的應用
* 
* 老大循環100次,而後老二循環10次,接着老三循環20次,
* 又接着老大循環100次,老二又循環10次,老三又循環20次,
* 如此循環50次,請寫出程序。
* 
* @author Liu
* @date 2018年1月15日 下午9:28:23 
*  
*/
public class ThreeConditionCommunication {
	public static void main(String[] args) {
		final Business business = new Business();
		
		new Thread(new Runnable() {
			public void run() {
				for(int i = 1; i <= 50; i++){
					try {
						business.sub2(i);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			public void run() {
				for(int i = 1; i <= 50; i++){
					try {
						business.sub3(i);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		for(int i = 1; i <= 50; i++){
			try {
				business.main(i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
//不一樣點:	
//condition.business
//business
	
	//將具備相似功能的業務代碼抽象到同一個類中
	//下面兩個方法(sub/main)是互斥的,均是獨立的一個同步模塊,處於同一個互斥組(經過synchronized代表均屬於同一個類,同一時刻只能有一個線程持有該鎖)
	static class Business{
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		Condition condition2 = lock.newCondition();
		Condition condition3 = lock.newCondition();
		
		//主線程和子線程切換執行的一個標識
		int flag = 1;
		
		void sub3(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(!flag){
				//循環和if功能相仿,但更加完備
				while(flag != 3){
					//使當前線程等待...
//					this.wait();
					condition3.await();
				}
				
				for(int j = 1; j <= 20; j++){
					System.out.println("sub3 thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = 1;
				
				//喚醒其它正在等待的線程
//				this.notify();
				condition.signal();
			} finally {
				lock.unlock();
			}
		}
		
		void sub2(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(!flag){
				//循環和if功能相仿,但更加完備
				while(flag != 2){
					//使當前線程等待...
//					this.wait();
					condition2.await();
				}
				
				for(int j = 1; j <= 10; j++){
					System.out.println("sub2 thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = 3;
				
				//喚醒其它正在等待的線程
//				this.notify();
				condition3.signal();
			} finally {
				lock.unlock();
			}
		}
		
		void main(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(flag){
				//循環和if功能相仿,但更加完備
				while(flag != 1){
					//使當前線程等待...
//					this.wait();
					condition.await();
				}
				
				for(int j = 1; j <= 100; j++){
					System.out.println("main thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = 2;
				
				//喚醒其它正在等待的線程
//				this.notify();
				condition2.signal();
			} finally {
				lock.unlock();
			}
		}
	}
}

3、jdk1.5提供的Lock與Condition實現的可阻塞隊列的應用案例

class BoundedBuffer {
    final Lock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition();
    final Condition notEmpty = lock.newCondition();

    final Object[] items = new Object[100];
    int putptr, takeptr, count;

    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) 
                  notFull.await();
            items[putptr] = x;
            if (++putptr == items.length) putptr = 0;
            ++count;
            notEmpty.signal();
        }
        finally {
            lock.unlock();
        }
    }
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) 
                  notEmpty.await();
            Object x = items[takeptr];
            if (++takeptr == items.length) takeptr = 0;
            --count;
            notFull.signal();
            return x;
        }
        finally {
            lock.unlock();
        }
    }
}

思考:

    一、爲何要用兩個Condition,而不是一個?測試

      喚醒的是取的,而不是放的,每次只能存取一個。若是隻用一個Condition,兩個放的都在等,一旦一個放的進去了,那麼它通知可能會致使另外一個放接着往下走。this

4、關鍵點

    一、Condition通常須要依賴Lock使用,不能獨立發揮做用。spa

    二、Condition做用至關於傳統線程中的wait()和notify()——線程間通訊。線程

    三、wait()和notify()只能在synchronized塊中使用,一樣地,Condition的await()和signal()方法只能在lock.lock()和lock.unlock()內部使用。設計

相關文章
相關標籤/搜索