線程通訊中的互斥除了用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();//解除鎖定 } }
接着上次(子線程和主線程依次輪流循環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(); } } } }
用上面的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(); } } } }