Java併發程式設計實戰--顯式的Condition物件

衣舞晨風發表於2017-06-06

     正如Lock是一種廣義的內建鎖,Condition也是一種廣義的內建條件佇列。

public interface Condition {
    void await() throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    //引數nanosTimeout為最長等待時間,單位為納秒;如果超時,則返回一個小於或等於 0 的值,
    //否則返回此方法返回時所剩餘時間的估計值,該值絕對小於 nanosTimeout 引數,
    //可以用此值來確定在等待返回但等待條件再次被打破的情況下,再次等待的時間,總共等待的時間絕對不超過nanosTimeout
    //(如果條件再次被打破時再等待nanosTimeout,則不需要使用此方法,直接使用await即可)
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    //免中斷等待:如果等待的執行緒被中斷,condition.await方法將丟擲一個InterruptedException異常。
    //如果你希望在出現這種情況時執行緒能夠繼續等待(等待條件滿足。似乎不太合理),
    //那麼可以使用condition.awaitUninterruptibly方法來代替await。
    void awaitUninterruptibly();
    boolean awaitUntil(Date deadline) throws InterruptedException;

    void signal();
    void signalAll();
}

     內建的條件佇列存在一些缺陷。每個內建鎖都只能有一個相關聯的條件佇列,因而在像BoundedBuffer這種類中:

@ThreadSafe
public abstract class BaseBoundedBuffer <V> {
    @GuardedBy("this") private final V[] buf;
    @GuardedBy("this") private int tail;
    @GuardedBy("this") private int head;
    @GuardedBy("this") private int count;

    protected BaseBoundedBuffer(int capacity) {
        this.buf = (V[]) new Object[capacity];
    }

    protected synchronized final void doPut(V v) {
        buf[tail] = v;
        if (++tail == buf.length)
            tail = 0;
        ++count;
    }

    protected synchronized final V doTake() {
        V v = buf[head];
        buf[head] = null;
        if (++head == buf.length)
            head = 0;
        --count;
        return v;
    }

    public synchronized final boolean isFull() {
        return count == buf.length;
    }

    public synchronized final boolean isEmpty() {
        return count == 0;
    }
}

@ThreadSafe
    public class BoundedBuffer <V> extends BaseBoundedBuffer<V> {
    // CONDITION PREDICATE: not-full (!isFull())
    // CONDITION PREDICATE: not-empty (!isEmpty())
    public BoundedBuffer() {
        this(100);
    }

    public BoundedBuffer(int size) {
        super(size);
    }

    // BLOCKS-UNTIL: not-full
    public synchronized void put(V v) throws InterruptedException {
        while (isFull())
            wait();
        doPut(v);
        notifyAll();
    }

    // BLOCKS-UNTIL: not-empty
    public synchronized V take() throws InterruptedException {
        while (isEmpty())
            wait();
        V v = doTake();
        notifyAll();
        return v;
    }

    // BLOCKS-UNTIL: not-full
    // Alternate form of put() using conditional notification
    public synchronized void alternatePut(V v) throws InterruptedException {
        while (isFull())
            wait();
        boolean wasEmpty = isEmpty();
        doPut(v);
        if (wasEmpty)
            notifyAll();
    }
}

     多個執行緒可能在同一條件佇列上等待不同的條件謂詞,並且在最常見的加鎖模式下公開條件佇列物件。這些因素都使得無法滿足在呼叫notifyAll時所有等待執行緒為同一型別的需求。如果想編寫一個帶有多個條件謂詞的併發物件,或者想獲得除了條件佇列可見性之外的更多控制權,就可以使用顯式的Lock和Condition而不是內建鎖和條件佇列,這是一種更靈活的選擇。

     一個Condition和一個Lock關聯在一起,就像一個條件佇列和一個內建鎖相關聯一樣。要建立一個Condition,可以在相關聯的Lock上呼叫Lock.newCondition方法。正如Lock比內建加鎖提供了更為豐富的功能,Condition同樣比內建條件佇列提供了更豐富的功能:在每個鎖上可存在多個等待、條件等待可以是可中斷的或不可中斷的、基於時限的等待,以及公平的或非公平的佇列操作。

     與內建條件佇列不同的是,對於每個Lock,可以有任意數量的Condition物件。Condition物件繼承了相關的Lock物件的公平性,對於公平的鎖,執行緒會依照FIFO順序從Condition.await中釋放。

特別注意:在Condition物件中,與wait、notify和notifyAll方法對應的分別是await、signal和signalAll。但是,Condition對Object進行了擴充套件,因而它也包含了wait和notify方法。一定要確保使用正確的版本——await和signal。

package net.jcip.examples;

import java.util.concurrent.locks.*;

import net.jcip.annotations.*;

/**
 * ConditionBoundedBuffer
 * <p/>
 * Bounded buffer using explicit condition variables
 *
 * @author Brian Goetz and Tim Peierls
 */

@ThreadSafe
public class ConditionBoundedBuffer <T> {
    protected final Lock lock = new ReentrantLock();
    // CONDITION PREDICATE: notFull (count < items.length)
    private final Condition notFull = lock.newCondition();
    // CONDITION PREDICATE: notEmpty (count > 0)
    private final Condition notEmpty = lock.newCondition();
    private static final int BUFFER_SIZE = 100;
    @GuardedBy("lock") private final T[] items = (T[]) new Object[BUFFER_SIZE];
    @GuardedBy("lock") private int tail, head, count;

    // BLOCKS-UNTIL: notFull
    public void put(T x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length)
                notFull.await();
            items[tail] = x;
            if (++tail == items.length)
                tail = 0;
            ++count;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    // BLOCKS-UNTIL: notEmpty
    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0)
                notEmpty.await();
            T x = items[head];
            items[head] = null;
            if (++head == items.length)
                head = 0;
            --count;
            notFull.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }
}

     ConditionBoundedBuffer與BoundedBuffer行為相同,但他對於條件佇列的使用,更容易理解–在分析使用多個Condition的類時,比分析一個使用單一內部佇列加多個條件謂詞的類簡單得多。**通過將兩個條件謂詞分開並放到兩個等待執行緒集,Condition使其更容易滿足單次通知的需求。**signal比signalAll更高效,它能極大地減少在每次快取操作中發生的上下文切換與鎖請求的次數。

     與內建鎖和條件佇列一樣,當使用顯式的Lock和Condition時,也必須滿足鎖、條件謂詞和條件變數之間的三元關係。在條件謂詞中包含的變數必須由Lock來保護,並且在檢查條件謂詞以及呼叫await和signal時,必須持有Lock物件。

     在使用顯式地Condition和內建條件佇列之間進行選擇時,與在ReentrantLock和synchronized之間進行選擇是一樣的:如果需要一些高階功能,例如使用公平的佇列操作或者在每個鎖上對應多個等待執行緒集,那麼應該優先使用Condition而不是內建的條件佇列。(如果需要ReentrantLock的高階功能,並且已經使用了它,那麼就已經做出了選擇。)

lock:需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做為鎖,多個執行緒中必須要使用一個ReentrantLock類做為物件才能保證鎖的生效。

Java併發程式設計實戰pdf及案例原始碼下載:
http://download.csdn.net/detail/xunzaosiyecao/9851028

作者:jiankunking 出處:http://blog.csdn.net/jiankunking

相關文章