Condition

一、Condition的簡介

    線程通訊中的互斥除了用synchronized、Object類的wait()和notify()/notifyAll()方式實現外,方法JDK1.5中提供的Condition配套Lock能夠實現相同的功能。Condition中的await()和signal()/signalAll()就至關於Object的wait()和notify()/notifyAll()。傳統線程的通訊方式,Condition均可以實現。不過要注意的是,Condition是被綁定到Lock上的,因此要建立一個Lock的Condition必須使用newCondition()方法。在等待Condition時,容許發生「虛假喚醒」, 這一般做爲對基礎平臺語義的讓步。java

    Condition的強大之處在於它能夠爲多個線程間創建不一樣的Condition。面試

二、Condition源碼中的例子算法

看JDK文檔中的一個例子:假定有一個綁定的緩衝區,它支持 put 和 take 方法。若是試圖在空的緩衝區上執行take 操做,則在某一個項變得可用以前,線程將一直阻塞;若是試圖在滿的緩衝區上執行 put 操做,則在有空間變得可用以前,線程將一直阻塞。咱們喜歡在單獨的等待 set 中保存put 線程和take 線程,這樣就能夠在緩衝區中的項或空間變得可用時利用最佳規劃,一次只通知一個線程。可使用兩個Condition實例來作到這一點。緩存

——其實就是java.util.concurrent.ArrayBlockingQueue的功能。多線程

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;  //寫索引  
  int takeptr; //讀索引  
  int 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 demo    

1)demo 1

        接着上次(子線程和主線程依次輪流循環50的多線程面試題)的一道面試題,如今改用Condition和Lock來實現:ide

/**
 * 面試題目:子線程循環10次,接着主線程循環100次,接着又回到子線程循環10次,接着又回到主線程又循環100次,如此循環50次。寫出程序
 * 
 * 經驗:要用到共同數據(包括同步鎖)或共同算法的若干個方法應該歸在同一個類身上,這種設計體現了高類聚和程序的健壯性。
 * 
 * 採用condition通訊方式:Condition的功能相似在傳統線程技術中的Object.wait()和Object.notify的功能。
 * 在等待Condition時,容許發生「虛假喚醒」。
 */
public class ConditionCommunication {
	public static void main(String[] args) {

		final Business business = new Business();

		// 子線程循環
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 50; i++) {
					try {
						business.sub(i);
					} catch (InterruptedException e) {
					}
				}
			}
		}).start();

		// 主線程循環
		for (int i = 1; i <= 50; i++) {
			try {
				business.mian(i);
			} catch (InterruptedException e) {
			}
		}

	}

	/**
	 * 業務類型(包含各色的同步鎖)
	 */
	static class Business {
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		// sub()方法是否該運行標識
		private boolean bShouldSub = true;

		/**
		 * 循環100次打印的方法sub()
		 * 
		 * @param i
		 * @throws InterruptedException
		 */
		public void sub(int i) throws InterruptedException {
			lock.lock();
			try {
				while (!bShouldSub) { // 當 bShouldSub 爲 false 時,則等待
					condition.await(); // 不能寫成condition.wait();,這裏的wait()是Object的方法
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println("sub thread :   第" + i + "行, 第" + j + "列");
				}
				bShouldSub = false; // 執行for循環後,標誌sub()方法不可再執行
				condition.signal(); // 發信號
			} finally {
				lock.unlock();
			}
		}

		/**
		 * 循環100次打印的方法mian()
		 * 
		 * @param i
		 * @throws InterruptedException
		 */
		public void mian(int i) throws InterruptedException {
			lock.lock();
			try {
				while (bShouldSub) {
					condition.await(); // 不能寫成condition.wait();,這裏的wait()是Object的方法
				}
				for (int j = 1; j <= 100; j++) {
					System.out.println("main thread :   第" + i + "行, 第" + j + "列");
				}
				bShouldSub = true; // 執行for循環後,標誌sub()方法可再執行了
				condition.signal(); // 發信號
			} finally {
				lock.unlock();
			}
		}
	}
}

2)demo 2

用上面的demo1題目改裝爲:線程1循環10次,接着線程2循環20次,接着線程3循環30次,又回到線程1循環10次,接着又回到線程2循環20次……,如此循環50次。spa

分析:這種實現順序喚醒的,能夠採用建立多個Condition來實現。.net

寫出程序:線程

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

/**
 * 面試題目:線程1循環10次,接着線程2循環20次,接着線程3循環30次,又回到線程1循環10次,接着又回到線程2循環20次……,如此循環50次。寫出程序
 */
public class ConditionCommunication2 {
	public static void main(String[] args) {

		final Business business = new Business();

		// 線程2循環sub2()
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 50; i++) {
					try {
						business.sub2(i);
					} catch (InterruptedException e) {
					}
				}
			}
		}).start();

		// 線程3循環sub3()
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 50; i++) {
					try {
						business.sub3(i);
					} catch (InterruptedException e) {
					}
				}
			}
		}).start();

		// (主線程)線程1循環sub1()
		for (int i = 1; i <= 50; i++) {
			try {
				business.sub1(i);
			} catch (InterruptedException e) {
			}
		}

	}

	/**
	 * 業務類型(包含各色的同步鎖)
	 */
	static class Business {
		Lock lock = new ReentrantLock();
		Condition condition1 = lock.newCondition();
		Condition condition2 = lock.newCondition();
		Condition condition3 = lock.newCondition();
		// sub()方法是否該運行標識
		private int shouldSub = 1;

		/**
		 * 循環10次打印的方法sub1()
		 * 
		 * @param i
		 * @throws InterruptedException
		 */
		public void sub1(int i) throws InterruptedException {
			lock.lock();
			try {
				while (shouldSub != 1) {  // 當 shouldSub 不爲 1 時,則等待
					condition1.await(); // 不能寫成condition.wait();,這裏的wait()是Object的方法
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println("sub1 thread :   第" + i + "行, 第" + j + "列");
				}
				shouldSub = 2; // 執行for循環後,標誌下一個能夠循環的方法時sub2()
				condition2.signal(); // condition2發信號
			} finally {
				lock.unlock();
			}
		}

		/**
		 * 循環20次打印的方法sub2()
		 * 
		 * @param i
		 * @throws InterruptedException
		 */
		public void sub2(int i) throws InterruptedException {
			lock.lock();
			try {
				while (shouldSub != 2) {  // 當 shouldSub 不爲 2 時,則等待
					condition2.await(); // 不能寫成condition.wait();,這裏的wait()是Object的方法
				}
				for (int j = 1; j <= 20; j++) {
					System.out.println("sub2 thread :   第" + i + "行, 第" + j + "列");
				}
				shouldSub = 3; // 執行for循環後,標誌下一個能夠循環的方法時sub3()
				condition3.signal(); // condition3發信號
			} finally {
				lock.unlock();
			}
		}

		/**
		 * 循環30次打印的方法sub3()
		 * 
		 * @param i
		 * @throws InterruptedException
		 */
		public void sub3(int i) throws InterruptedException {
			lock.lock();
			try {
				while (shouldSub != 3) {  // 當 shouldSub 不爲 3時,則等待
					condition3.await(); // 不能寫成condition.wait();,這裏的wait()是Object的方法
				}
				for (int j = 1; j <= 30; j++) {
					System.out.println("sub3 thread :   第" + i + "行, 第" + j + "列");
				}
				shouldSub = 1; // 執行for循環後,標誌下一個能夠循環的方法時sub1()
				condition1.signal(); // condition1發信號
			} finally {
				lock.unlock();
			}
		}
	}
}
相關文章
相關標籤/搜索