在看Condition之前,我們先來看下面這個例子:
工廠類,用來存放、取出商品:
public class Depot { private int depotSize; //倉庫大小 private Lock lock; //獨占鎖 public Depot(){ depotSize = 0; lock = new ReentrantLock(); } /** * 商品入庫 * @param value */ public void put(int value){ try { lock.lock(); depotSize += value; System.out.println(Thread.currentThread().getName() + " put " + value +" ----> the depotSize: " + depotSize); } finally{ lock.unlock(); } } /** * 商品出庫 * @param value */ public void get(int value){ try { lock.lock(); depotSize -= value; System.out.println(Thread.currentThread().getName() + " get " + value +" ----> the depotSize: " + depotSize); } finally{ lock.unlock(); } } }
生產者,生產商品,往倉庫里面添加商品:
public class Producer { private Depot depot; public Producer(Depot depot){ this.depot = depot; } public void produce(final int value){ new Thread(){ public void run(){ depot.put(value); } }.start(); } }
消費者,消費商品,從倉庫里面取出商品:
public class Customer { private Depot depot; public Customer(Depot depot){ this.depot = depot; } public void consume(final int value){ new Thread(){ public void run(){ depot.get(value); } }.start(); } }
測試類:
public class Test { public static void main(String[] args) { Depot depot = new Depot(); Producer producer = new Producer(depot); Customer customer = new Customer(depot); producer.produce(10); customer.consume(5); producer.produce(20); producer.produce(5); customer.consume(35); } }
運行結果:
Thread-0 put 10 ----> the depotSize: 10 Thread-1 get 5 ----> the depotSize: 5 Thread-2 put 20 ----> the depotSize: 25 Thread-3 put 5 ----> the depotSize: 30 Thread-4 get 35 ----> the depotSize: -5
程序的運行結果是沒有錯誤的,先put10、然后get5、put20、put5、get35。程序運行結果非常正確,但是在現實生活中,這個實例存在兩處錯誤:
第一:倉庫的容量是有限的,我們不可能無限制的往倉庫里面添加商品。
第二:倉庫的容量是不可能為負數的,但是最后的結果為-5,與現實存在沖突。
針對於上面兩處錯誤,怎么解決?這就輪到Condition大顯神通了。
Condition
通過前面幾篇博客我們知道Lock提供了比synchronized更加強大、靈活的鎖機制,它從某種程度上來說替代了synchronized方式的使用。Condition從字面上面理解就是條件。對於線程而言它為線程提供了一個含義,以便在某種狀態(條件Condition)可能為true的另一個線程通知它之前,一直掛起該線程。
對於Condition,JDK API中是這樣解釋的:
Condition 將 Object 監視器方法(wait、notify 和 notifyAll)分解成截然不同的對象,以便通過將這些對象與任意 Lock 實現組合使用,為每個對象提供多個等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 監視器方法的使用。
條件(也稱為條件隊列 或條件變量)為線程提供了一個含義,以便在某個狀態條件現在可能為 true 的另一個線程通知它之前,一直掛起該線程(即讓其“等待”)。因為訪問此共享狀態信息發生在不同的線程中,所以它必須受保護,因此要將某種形式的鎖與該條件相關聯。等待提供一個條件的主要屬性是:以原子方式 釋放相關的鎖,並掛起當前線程,就像 Object.wait 做的那樣。
Condition 實例實質上被綁定到一個鎖上。要為特定 Lock 實例獲得 Condition 實例,請使用其newCondition() 方法。下面我們通過Condition來解決上面的問題:這里只改倉庫Depot的代碼:
public class Depot { private int depotSize; //倉庫大小 private Lock lock; //獨占鎖 private int capaity; //倉庫容量 private Condition fullCondition; private Condition emptyCondition; public Depot(){ this.depotSize = 0; this.lock = new ReentrantLock(); this.capaity = 15; this.fullCondition = lock.newCondition(); this.emptyCondition = lock.newCondition(); } /** * 商品入庫 * @param value */ public void put(int value){ lock.lock(); try { int left = value; while(left > 0){ //庫存已滿時,“生產者”等待“消費者”消費 while(depotSize >= capaity){ fullCondition.await(); } //獲取實際入庫數量:預計庫存(倉庫現有庫存 + 生產數量) > 倉庫容量 ? 倉庫容量 - 倉庫現有庫存 : 生產數量 // depotSize left capaity capaity - depotSize left int inc = depotSize + left > capaity ? capaity - depotSize : left; depotSize += inc; left -= inc; System.out.println(Thread.currentThread().getName() + "----要入庫數量: " + value +";;實際入庫數量:" + inc + ";;倉庫貨物數量:" + depotSize + ";;沒有入庫數量:" + left); //通知消費者可以消費了 emptyCondition.signal(); } } catch (InterruptedException e) { } finally{ lock.unlock(); } } /** * 商品出庫 * @param value */ public void get(int value){ lock.lock(); try { int left = value; while(left > 0){ //倉庫已空,“消費者”等待“生產者”生產貨物 while(depotSize <= 0){ emptyCondition.await(); } //實際消費 倉庫庫存數量 < 要消費的數量 ? 倉庫庫存數量 : 要消費的數量 int dec = depotSize < left ? depotSize : left; depotSize -= dec; left -= dec; System.out.println(Thread.currentThread().getName() + "----要消費的數量:" + value +";;實際消費的數量: " + dec + ";;倉庫現存數量:" + depotSize + ";;有多少件商品沒有消費:" + left); //通知生產者可以生產了 fullCondition.signal(); } } catch (InterruptedException e) { e.printStackTrace(); } finally{ lock.unlock(); } } }
test:
public class Test { public static void main(String[] args) { Depot depot = new Depot(); Producer producer = new Producer(depot); Customer customer = new Customer(depot); producer.produce(10); customer.consume(5); producer.produce(15); customer.consume(10); customer.consume(15); producer.produce(10); } }
運行結果:
Thread-0----要入庫數量: 10;;實際入庫數量:10;;倉庫貨物數量:10;;沒有入庫數量:0 Thread-1----要消費的數量:5;;實際消費的數量: 5;;倉庫現存數量:5;;有多少件商品沒有消費:0 Thread-4----要消費的數量:15;;實際消費的數量: 5;;倉庫現存數量:0;;有多少件商品沒有消費:10 Thread-2----要入庫數量: 15;;實際入庫數量:15;;倉庫貨物數量:15;;沒有入庫數量:0 Thread-4----要消費的數量:15;;實際消費的數量: 10;;倉庫現存數量:5;;有多少件商品沒有消費:0 Thread-5----要入庫數量: 10;;實際入庫數量:10;;倉庫貨物數量:15;;沒有入庫數量:0 Thread-3----要消費的數量:10;;實際消費的數量: 10;;倉庫現存數量:5;;有多少件商品沒有消費:0
在Condition中,用await()替換wait(),用signal()替換 notify(),用signalAll()替換notifyAll(),對於我們以前使用傳統的Object方法,Condition都能夠給予實現。