JUC blocking queue of DelayQueue source code analysis

DelayQueue is a delay obtaining support elements *** blocking queue. And queue Delayed elements must implement the interface. When you create an element can specify how long it takes to get the current element from the queue. Only to get an element from the queue when the delay has expired. DelayQueue very wide range of applications, such as the validity period can be used to hold it in the cache elements, which can also be used to implement the timing task.

Delayed Interface

Before analyzing DelayQueue source, we take a look at Delayd interface, the source code is defined as follows:

public interface Delayed extends Comparable < Delayed > {

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

We see, Delayed interface inherits the Comparable interface, that is, to achieve Delayed object interface must implement getDelay (TimeUnit unit) method and compareTo (T o) method. Here compareTo (T o) method may be used to implement sorting element, a long delay time can be put at the end of the queue.

DelayQueue Constructor

Analysis of the Delayed interfaces above, then we analyze DelayQueue constructor. DelayQueue provides two constructors, one is no argument constructor, a given set parameter is the constructor. Its source code is as follows:

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

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

addAll method is a method AbstractQueue abstract class, its source code is as follows:

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;
}

From the above code, we can see that, AbstractQueue abstract class addAll method is actually invoked DelayQueue class add methods to achieve.

Enqueue operation DelayQueue

Providing DelayQueue 4 enqueue operation, namely:

  • add (E e): obstruction will develop elements to delay the queue to go, because the queue is *** This method therefore never blocked.
  • offer (E e): obstruction will develop elements to delay the queue to go, because the queue is *** This method therefore never blocked.
  • put (E e): obstruction will develop elements to delay the queue to go, because the queue is *** This method therefore never blocked.
  • offer (E e, long timeout, TimeUnit unit): obstruction will develop elements to delay the queue to go, because the queue is *** This method therefore never blocked.

Here you might wonder why these methods of interpretation into the columns are the same? The first wait for the next question to answer, let's take a look at these methods into the column of Source Definition:

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);
}

Here we can see from the source code, the Add (E E) method, PUT (E E) methods and offer (E e, long timeout, TimeUnit unit) methods are invoked offer (E e) method to achieve, which is explain why these methods are the same reasons. Which offer (E e) core of the method is called PriorityQueue the offer (E e) method, PriorityQueue and PriorityBlockingQueue are based on binary heap *** queue, but PriorityQueue not blocked and PriorityBlockingQueue is blocked.

Dequeue operation DelayQueue

DelayQueue provides a method of operation of the column 3, which are:

  • poll (): retrieve and delete the beginning of this queue, if the queue without delay of the delay element, or null
  • take (): retrieve the head of this queue is removed and, if necessary, wait until the delay has expired on the queue element is available.
  • poll (long timeout, TimeUnit unit) : retrieve and delete the head of this queue, if necessary, wait until the delay has expired elements available or specified wait time or the queue expires.

Let's analyze one by one the original column operation.

poll():

Source Definition poll operation is as follows:

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();
    }
}

The only difference with the method PriorityQueue poll method is more if (first == null || first.getDelay ( NANOSECONDS)> 0) This condition is determined, this condition is in the queue if the queue is not the element or elements unexpired , null is returned.

take

take operation source is defined as follows:

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();
    }
}

take operations than poll operation is slightly more complicated, but the logic is relatively simple. Only when the element acquired delay time to check the remaining elements, if the remaining delay time <= 0, the first element directly returned to the queue. If the remaining delay time> 0, it is determined whether the leader is null, Ruoguo leader is not null, it indicates that the thread has been waiting in the queue waiting to acquire the header element, thus directly into the waiting queue. Ruoguo leader is null, said this is the first element to obtain the head of the thread, the thread assigned to the current leader, and then wait for a timeout remaining delay time. In the take operation point to note is FIST = null , because if the first is not set to null, then will cause memory overflow exception, because at the time of concurrent, each thread will hold a first, and therefore will not be first release, Ruoguo too many threads, it will lead to memory overflow exception.

poll(long timeout, TimeUnit unit)

Timeout waiting to acquire source queue elements as follows:

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();
    }
}

The dequeue logic operation and take out the logical operation of the column is almost the same, the only difference is that take is not timeout, and the operation is timed out waiting for change.

to sum up

DelayQueue enqueue and dequeue operation logic is relatively simple, that is, when acquiring the element, determining whether the element has expired, Ruoguo expired can be directly obtained, not expired, then pollthe operation is directly returned null, Take queue operation is entered to wait wait.

Guess you like

Origin blog.51cto.com/14570694/2443986