java的java.util.concurrent.locks包內有Condition接口,該接口的官方定義以下:html
Condition
factors out the Object
monitor methods (wait
, notify
and notifyAll
) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock
implementations. Where a Lock
replaces the use of synchronized
methods and statements, a Condition
replaces the use of the Object monitor methods.java
Conditions (also known as condition queues or condition variables) provide a means for one thread to suspend execution (to "wait") until notified by another thread that some state condition may now be true. Because access to this shared state information occurs in different threads, it must be protected, so a lock of some form is associated with the condition. The key property that waiting for a condition provides is that it atomically releases the associated lock and suspends the current thread, just like Object.wait
.api
咱們經過一個實際的例子來解釋Condition的用法:併發
咱們要打印1到9這9個數字,由A線程先打印1,2,3,而後由B線程打印4,5,6,而後再由A線程打印7,8,9. 這道題有不少種解法,如今咱們使用Condition來作這道題(使用Object的wait,notify方法的解法在這裏)。oracle
package cn.outofmemory.locks; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class App { static class NumberWrapper { public int value = 1; } public static void main(String[] args) { //初始化可重入鎖 final Lock lock = new ReentrantLock(); //第一個條件當屏幕上輸出到3 final Condition reachThreeCondition = lock.newCondition(); //第二個條件當屏幕上輸出到6 final Condition reachSixCondition = lock.newCondition(); //NumberWrapper只是爲了封裝一個數字,一邊能夠將數字對象共享,並能夠設置爲final //注意這裏不要用Integer, Integer 是不可變對象 final NumberWrapper num = new NumberWrapper(); //初始化A線程 Thread threadA = new Thread(new Runnable() { @Override public void run() { //須要先得到鎖 lock.lock(); try { System.out.println("threadA start write"); //A線程先輸出前3個數 while (num.value <= 3) { System.out.println(num.value); num.value++; } //輸出到3時要signal,告訴B線程能夠開始了 reachThreeCondition.signal(); } finally { lock.unlock(); } lock.lock(); try { //等待輸出6的條件 reachSixCondition.await(); System.out.println("threadA start write"); //輸出剩餘數字 while (num.value <= 9) { System.out.println(num.value); num.value++; } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { try { lock.lock(); while (num.value <= 3) { //等待3輸出完畢的信號 reachThreeCondition.await(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } try { lock.lock(); //已經收到信號,開始輸出4,5,6 System.out.println("threadB start write"); while (num.value <= 6) { System.out.println(num.value); num.value++; } //4,5,6輸出完畢,告訴A線程6輸出完了 reachSixCondition.signal(); } finally { lock.unlock(); } } }); //啓動兩個線程 threadB.start(); threadA.start(); } }
上述代碼中有完整的註釋,請參考註釋,理解Condition的用法。app
基本思路就是首先要A線程先寫1,2,3,這時候B線程應該等待reachThredCondition信號,而當A線程寫完3以後就經過 signal告訴B線程「我寫到3了,該你了」,這時候A線程要等嗲reachSixCondition信號,同時B線程獲得通知,開始寫4,5,6,寫 完4,5,6以後B線程通知A線程reachSixCondition條件成立了,這時候A線程就開始寫剩下的7,8,9了。dom
爲了更好的理解Condition的用法,咱們再看下java官方提供的例子:ide
package locks; import java.util.Random; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class AppOfficial { /** * BoundedBuffer 是一個定長100的集合,當集合中沒有元素時,take方法須要等待,直到有元素時才返回元素 * 當其中的元素數達到最大值時,要等待直到元素被take以後才執行put的操做 * @author yukaizhao * */ static 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 { System .out.println("put wait lock"); lock.lock(); System.out.println("put get lock"); try { while (count == items.length) { System.out.println("buffer full, please wait"); notFull.await(); } items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { System.out.println("take wait lock"); lock.lock(); System.out.println("take get lock"); try { while (count == 0) { System.out.println("no elements, please wait"); notEmpty.await(); } Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } } public static void main(String[] args) { final BoundedBuffer boundedBuffer = new BoundedBuffer(); Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("t1 run"); for (int i=0;i<1000;i++) { try { System.out.println("putting.."); boundedBuffer.put(Integer.valueOf(i)); } catch (InterruptedException e) { e.printStackTrace(); } } } }) ; Thread t2 = new Thread(new Runnable() { @Override public void run() { for (int i=0;i<1000;i++) { try { Object val = boundedBuffer.take(); System.out.println(val); } catch (InterruptedException e) { e.printStackTrace(); } } } }) ; t1.start(); t2.start(); } }
這個示例中BoundedBuffer是一個固定長度的集合,這個在其put操做時,若是發現長度已經達到最大長度,那麼會等待notFull信 號,若是獲得notFull信號會像集合中添加元素,併發出notEmpty的信號,而在其take方法中若是發現集合長度爲空,那麼會等待 notEmpty的信號,同時若是拿到一個元素,那麼會發出notFull的信號。this