【Java並發編程實戰】-----“J.U.C”:Condition


在看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都能夠給予實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM