DelayQueueのソースコード解析のJUCブロッキングキュー

DelayQueueは、支持要素***のブロッキングキューを取得する遅延です。そして、キュー遅延要素は、インタフェースを実装する必要があります。あなたは要素を作成すると、それはキューから現在の要素を取得するのにかかる時間の長さを指定することができます。遅延が経過したときのみ、キューから要素を取得します。そのような有効期間のようなアプリケーションのDelayQueue非常に広い範囲も、タイミングタスクを実装するために使用することができるキャッシュ要素、それを保持するために使用することができます。

遅延インタフェース

DelayQueueソースを分析する前に、我々はDelaydインターフェイスを見て、次のように、ソースコードが定義されています。

public interface Delayed extends Comparable < Delayed > {

    /**
     * 指定返回对象的延时时间
     * @param  unit [时间单位]
     * @return      [延时的剩余,0或者-1表示延时已经过期]
     */
    long getDelay(TimeUnit unit);
}

我々は、実装する必要が遅れオブジェクトインタフェースを達成するために、すなわち、遅延インターフェースが同等のインタフェースを継承し、参照getDelay(TimeUnitでユニット)メソッドとのcompareTo(T O)方法。ここでのcompareTo(T O)メソッドは、ソート要素を実装するために使用することができる、長い遅延時間は、キューの末尾に置くことができます。

DelayQueueコンストラクタ

上記遅延インターフェイスの分析、我々はDelayQueueコンストラクタを分析します。DelayQueueは、一つは、与えられたパラメータセットコンストラクタで、引数のコンストラクタはありません、2つのコンストラクタを提供します。次のようにそのソースコードは次のとおりです。

/**
 * 构建一个空的DelayQueue
 */
public DelayQueue() {}

/**
 * 给定集合c为参数的构造函数
 * 将集合c中的元素全部放入到DelayQueue中
 */
public DelayQueue(Collection < ? extends E > c) {
    this.addAll(c);
}

addAllメソッドは次のようにメソッドAbstractQueue抽象クラスは、そのソースコードは次のとおりです。

public boolean addAll(Collection < ? extends E > c) {
    // 参数检测
    if (c == null)
        throw new NullPointerException();
    if (c == this)
        throw new IllegalArgumentException();
    boolean modified = false;
    //遍历集合c中的元素
    for (E e: c)
        // 调用DelayQueue中的add方法
        if (add(e))
            modified = true;
    return modified;
}

上記のコードから、我々はAbstractQueue抽象クラス、ことがわかりますがaddAllのメソッドが実際に呼び出されるDelayQueueクラスを追加達成するための方法を。

エンキュー操作DelayQueue

すなわち、DelayQueue 4エンキュー操作を提供します。

  • (E eを)追加:キューがあるため、障害物は、この方法は、したがって、ブロックされたことはありません***、行くためのキューを遅らせるための要素を開発します。
  • 提供(EのE):障害物はキューが***この方法は、したがって、ブロックされることはありませんので、どこへ行くのキューを遅らせるための要素を開発します。
  • (E eを)置く:キューが***この方法は、したがって、ブロックされることはありませんので、障害物は、行くためにキューを遅らせるための要素を開発します。
  • 提供(E電子、長いタイムアウト、 TimeUnitで単位): 障害物はキューが***この方法は、したがって、ブロックされることはありませんので、どこへ行くのキューを遅らせるための要素を開発します。

列に解釈するこれらの方法が同じである理由はここにあなたは不思議に思うかもしれませんか?次の質問のは、ソースの定義の欄に、これらの方法を見てみましょう、回答するための最初の待ち時間:

public boolean add(E e) {
    return offer(e);
}

public boolean offer(E e) {
    //获取可重入锁
    final ReentrantLock lock = this.lock;
    //加锁
    lock.lock();
    try {
        //调用PriorityQueue中的offer方法
        q.offer(e);
        //调用PriorityQueue中的peek方法
        if (q.peek() == e) {
            leader = null;
            available.signal();
        }
        return true;
    } finally {
        //释放锁
        lock.unlock();
    }
}

public void put(E e) {
    offer(e);
}

public boolean offer(E e, long timeout, TimeUnit unit) {
    return offer(e);
}

私たちは、ソースコードから見ることができますここで、[追加(E E)法、PUT(E E)メソッドと提供(E電子、長いタイムアウト、 TimeUnitでユニット) メソッドが呼び出されるプラン(E E)で達成するための方法を、これらのメソッドは、同じ理由である理由を説明します。これは(E e)の提供方法のコアが優先度つきキューと呼ばれるプラン(E e)の方法、優先度つきキューとPriorityBlockingQueueキュー***バイナリヒープに基づいていますが、優先度つきキューブロックされ、PriorityBlockingQueueがブロックされていません。

デキュー操作DelayQueue

DelayQueueは、カラム3の動作方法を提供します。

  • 世論調査():遅延素子の遅延、またはnullなしキュー場合は、このキューの先頭を取得および削除
  • :()かかり、遅延がキュー要素に満了するまで使用可能です待つ必要があれば、削除されます。このキューの先頭を取得して。
  • 世論調査(long timeout、TimeUnit unit)指定 : 遅延が利用できる要素または指定された待機時間が経過したか、キューの有効期限が切れるまで取得し、必要に応じて、このキューの先頭を削除し、待ちます。

のは、1つのオリジナル列操作によるものを分析してみましょう。

世論調査():

次のようにソースの定義のポーリング操作は次のとおりです。

public E poll() {
   //获取可重入锁
    final ReentrantLock lock = this.lock;
    //加锁
    lock.lock();
    try {
       //获取队列中的第一个元素
        E first = q.peek();
        //若果元素为null,或者头元素还未过期,则返回false
        if (first == null || first.getDelay(NANOSECONDS) > 0)
            return null;
        else
           //调用PriorityQueue中的出列方法
            return q.poll();
    } finally {
        lock.unlock();
    }
}

メソッド優先度つきキューのポーリング方法との唯一の違いは、より多くの場合(第== NULL || first.getDelay(ナノ秒)> 0) キューがない要素または有効期限内の要素がある場合、この条件が決定され、この状態は、キューにあります、nullが返されます。

取る

次のように取る動作ソースが定義されています。

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    //加锁
    lock.lockInterruptibly();
    try {
        //西循环
        for (;;) {
            //查看队列头元素
            E first = q.peek();
            //如果队列头元素为null,则表示队列中没有数据,线程进入等待队列
            if (first == null)
                available.await();
            else {
                // 获取first元素剩余的延时时间
                long delay = first.getDelay(NANOSECONDS);
                //若果剩余延时时间<=0 表示元素已经过期,可以从队列中获取元素
                if (delay <= 0)
                    //直接返回头部元素
                    return q.poll();
                //若果剩余延时时间>0,表示元素还未过期,则将first置为null,防止内存溢出
                first = null; // don't retain ref while waiting
                //如果leader不为null,则直接进入等待队列中等待
                if (leader != null)
                    available.await();
                else {
                    //若果leader为null,则把当前线程赋值给leader,并超时等待delay纳秒
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;
                    try {
                        available.awaitNanos(delay);
                    } finally {
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
        if (leader == null && q.peek() != null)
            //唤醒线程
            available.signal();
        lock.unlock();
    }
}

取るポーリング操作よりも操作が少し複雑ですが、ロジックは比較的簡単です。要素が残りの要素をチェックするために遅延時間を取得した場合にのみ、残りの遅延時間<= 0の場合、最初の要素は直接キューに戻します。> 0の残りの遅延時間は、それがRuoguoリーダーがnullでない、リーダーがヌルであるか否かが判断される場合は、スレッドは待ち行列にこのように直接、ヘッダ要素を取得するために待ち行列で待っていることを示しています。Ruoguoリーダーがヌルである、これはスレッドの頭、現在のリーダーに割り当てられたスレッドを取得し、その後、遅延時間を残りのタイムアウトを待機する最初の要素であることを特徴とします。テイク動作点に注意することであるFIST =ヌル最初はヌルに設定されていない場合に同時の時点で、各スレッドは、最初を保持し、したがって最初にないので、次に、メモリのオーバーフロー例外が発生するので、リリース、Ruoguoあまりにも多くのスレッドが、それはメモリオーバーフロー例外につながります。

世論調査(長いタイムアウト、TimeUnitでユニット)

次のようにソース・キュー要素を取得するために待機タイムアウト:

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        for (;;) {
            E first = q.peek();
            if (first == null) {
                if (nanos <= 0)
                    return null;
                else
                    nanos = available.awaitNanos(nanos);
            } else {
                long delay = first.getDelay(NANOSECONDS);
                if (delay <= 0)
                    return q.poll();
                if (nanos <= 0)
                    return null;
                first = null; // don't retain ref while waiting
                if (nanos < delay || leader != null)
                    nanos = available.awaitNanos(nanos);
                else {
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;
                    try {
                        long timeLeft = available.awaitNanos(delay);
                        nanos -= delay - timeLeft;
                    } finally {
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
        if (leader == null && q.peek() != null)
            available.signal();
        lock.unlock();
    }
}

デキュー論理演算と取る列の論理演算を行うには、唯一の違いは、ある、ほぼ同じであるテイクがタイムアウトではなく、操作が変更を待ってタイムアウトしています。

概要

DelayQueueのエンキューおよびデキュー操作ロジックは、要素の期限が切れているかどうかを決定、要素を取得するときには、Ruoguoは直接、その後、期限が切れていない、取得することができる有効期限が切れている、比較的簡単でpoll、操作が直接nullを返されます待つように入力されたキューの操作を待ちます。

おすすめ

転載: blog.51cto.com/14570694/2443986