9. DelayQueue

DelayQueue类实现BlockingQueue接口。阅读BlockingQueue文本以获取有关的更多信息。

DelayQueue内部阻止元素直到某个延迟到期,元素必须实现接口java.util.concurrent.Delayed。以下是java.util.concurrent.Delayed接口:

public interface Delayed extends Comparable

getDelay()方法返回的值应该是在释放此元素之前剩余的延迟。如果返回0或负值,则延迟将被视为已过期,并且在DelayQueue调用下一个take()等操作时释放。

传递给getDelay()方法的TimeUnit实例是一个Enum,它说明了延迟的时间单位。TimeUnit枚举有以下值:

DAYS
HOURS
MINUTES
SECONDS
MILLISECONDS
MICROSECONDS
NANOSECONDS

Delayed接口继承了java.lang.Comparable接口,这意味着Delayed对象可以被相互比较。这可能是在DelayQueue内部用于排序队列中的元素,因此它们能够按到期时间排序释放。

以下是使用DelayQueue的示例:

public class DelayQueueExample {

    public static void main(String[] args) {
        DelayQueue queue = new DelayQueue();
        Delayed element1 = new DelayedElement();
        queue.put(element1);
        Delayed element2 = queue.take();
    }
}

DelayedElement是我创建的Delayed接口的实现。它不是java.util.concurrent包的一部分。你必须创建自己的Delayed接口实现才能使用DelayQueue类。

源码

DelayQueue类的泛型定义中可以看出,此类只能储存继承自Delayed接口的元素,内部使用一个优先级队列对元素进行排序。

public class DelayQueue extends AbstractQueue
    implements BlockingQueue {

    private final transient ReentrantLock lock = new ReentrantLock();
    private final PriorityQueue q = new PriorityQueue();

    // 等待队列的头节点,可以视作一个缓存
    // 当一个线程成为leader,它只会等待指定延迟的时间,但
    // 其他线程会一直等到。所以leader线程在获取到元素后
    // 一定要释放其他线程,除非其他线程临时成为leader
    private Thread leader;

    /**
     * 当队列头部的一个新元素可获得(即超时到期)或者一个新线程成为leader,唤醒此等待条件上的线程
     */
    private final Condition available = lock.newCondition();

构造函数

只有两个构造方法,一个是默认构造方法,一个是给定一个集合,并将其中元素增加到等待队列中。

public DelayQueue() {}

/**
 * Creates a {@code DelayQueue} initially containing the elements of the
 * given collection of {@link Delayed} instances.
 *
 * @param c the collection of elements to initially contain
 * @throws NullPointerException if the specified collection or any
 *         of its elements are null
 */
public DelayQueue(Collection c) {
    this.addAll(c);
}

增加操作

public boolean add(E e) {
    // 重用offer方法
    return offer(e);
}

public boolean offer(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 将元素增加到优先级队列中
        q.offer(e);
        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);
}

删除操作

public E remove() {
    E x = poll();
    if (x != null)
        return x;
    else
        throw new NoSuchElementException();
}

// 提取并删除第一个元素,如果队列为空返回null
public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 获取第一个元素
        E first = q.peek();
        return (first == null || first.getDelay(NANOSECONDS) > 0)
            ? null
            : q.poll();
    } finally {
        lock.unlock();
    }
}

/**
 * 提取并删除队列的第一个元素,如果队列为空则等待 
 * 直到有可获得的元素
 *
 * @return the head of this queue
 * @throws InterruptedException {@inheritDoc}
 */
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        for (;;) {
            E first = q.peek();
            // 如果队列为空,阻塞
            if (first == null)
                available.await();
            else {
                // 获取头元素的等待延迟
                long delay = first.getDelay(NANOSECONDS);
                if (delay <= 0L)
                    return q.poll();
                first = null; // don't retain ref while waiting
                // 如果已经有线程在等待获取头元素,那么阻塞自己
                if (leader != null)
                    available.await();
                // 否则,自己就是leader,等待给定延迟
                else {
                    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();
    }
}

/**
 * Retrieves and removes the head of this queue, waiting if necessary
 * until an element with an expired delay is available on this queue,
 * or the specified wait time expires.
 *
 * @return the head of this queue, or {@code null} if the
 *         specified waiting time elapses before an element with
 *         an expired delay becomes available
 * @throws InterruptedException {@inheritDoc}
 */
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 <= 0L)
                    return null;
                else
                    nanos = available.awaitNanos(nanos);
            } else {
                long delay = first.getDelay(NANOSECONDS);
                if (delay <= 0L)
                    return q.poll();
                // 如果延迟还未到期,而指定的超时已到期,那么返回null
                if (nanos <= 0L)
                    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();
    }
}

访问操作

public E element() {
    E x = peek();
    if (x != null)
        return x;
    else
        throw new NoSuchElementException();
}

public E peek() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 委托给优先级队列获取
        return q.peek();
    } finally {
        lock.unlock();
    }
}

其他操作

public int drainTo(Collection c) {
    return drainTo(c, Integer.MAX_VALUE);
}

public int drainTo(Collection c, int maxElements) {
    Objects.requireNonNull(c);
    if (c == this)
        throw new IllegalArgumentException();
    if (maxElements <= 0)
        return 0;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        int n = 0;
        for (E first;
             n < maxElements
                 && (first = q.peek()) != null
                 && first.getDelay(NANOSECONDS) <= 0;) {
            // 增加到集合中
            c.add(first);   // In this order, in case add() throws.
            // 从队列中删除此元素
            q.poll();
            ++n;
        }
        return n;
    } finally {
        lock.unlock();
    }
}

迭代器

迭代器使用数组保存队列中的元素,当创建一个迭代器时,使用toArray()方法将当前队列转换为数组,所以此迭代器不一定会和内部的优先级队列保持一致。迭代器除了提供访问操作外,只提供了一个删除操作,这个删除操作保证不会出现不一致的情况。

public Iterator iterator() {
    return new Itr(toArray());
}

/**
 * Snapshot iterator that works off copy of underlying q array.
 */
private class Itr implements Iterator {
    final Object[] array; // Array of all elements
    int cursor;           // index of next element to return
    int lastRet;          // index of last element, or -1 if no such

    Itr(Object[] array) {
        lastRet = -1;
        this.array = array;
    }

    public boolean hasNext() {
        return cursor < array.length;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        if (cursor >= array.length)
            throw new NoSuchElementException();
        return (E)array[lastRet = cursor++];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        removeEQ(array[lastRet]);
        lastRet = -1;
    }
}

void removeEQ(Object o) {
    final ReentrantLock lock = this.lock;
    // 加锁
    lock.lock();
    try {
        // 获取优先级队列的迭代器,然后执行删除操作
        for (Iterator it = q.iterator(); it.hasNext(); ) {
            if (o == it.next()) {
                it.remove();
                break;
            }
        }
    } finally {
        lock.unlock();
    }
}

示例:

import org.junit.Assert;
import org.junit.Test;

import java.util.*;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class DelayQueueTest {
    private DelayQueue queue;
    private int count;

    // 测试迭代器和内部组件的不一致性

    @Test
    public void test() {
        List list = new ArrayList<>();
        for(int i = 1; i < 6; ++i) {
            list.add(new DelayElement(i, TimeUnit.SECONDS));
        }
        queue = new DelayQueue<>(list);

        Iterator iterator = queue.iterator();
        // 增加一个元素
        queue.add(new DelayElement(6, TimeUnit.SECONDS));

        iterator.forEachRemaining((e) -> ++count);
        Assert.assertEquals(count, queue.size());

        iterator.next();
        iterator.remove();
        System.out.println(queue.size());
    }

    // 测试reomove方法的一致性
    @Test
    public void testRemoveInItr() {
        List list = new ArrayList<>();
        for(int i = 1; i < 6; ++i) {
            list.add(new DelayElement(i, TimeUnit.SECONDS));
        }
        queue = new DelayQueue<>(list);

        Iterator iterator = queue.iterator();
        // 增加一个元素
        queue.add(new DelayElement(6, TimeUnit.SECONDS));

        System.out.println(queue.size());
        iterator.next();
        iterator.remove();
        System.out.println(queue.size());
    }


    private static class DelayElement implements Delayed {
        private long deadline;

        DelayElement(long delay) {
            this.deadline = System.nanoTime() + delay;
        }

        DelayElement(long delay, TimeUnit unit) {
            this.deadline = System.nanoTime() + unit.toNanos(delay);
        }

        DelayElement(Date date) {
            this.deadline = TimeUnit.MILLISECONDS.toNanos(date.getTime());
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return unit.toNanos(deadline - System.nanoTime());
        }

        @Override
        public int compareTo(Delayed o) {
            Objects.requireNonNull(o);
            return (int) (deadline - o.getDelay(TimeUnit.NANOSECONDS));
        }
    }
}

输出:

java.lang.AssertionError:
Expected :5
Actual :6

at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
at org.junit.Assert.assertEquals(Assert.java:631)
at p6.DelayQueueTest.test(DelayQueueTest.java:28)

6
5

核心要点

  1. 使用此队列时,元素必须要实现Delayed接口
  2. 当已经有一个线程等待获取队列头元素时,其他也想要获取元素的线程就会进行等待阻塞状态
  3. 迭代器不和内部的优先级队列保持一致性
  4. 迭代器的remove()方法与内部的优先级队列保持一致性

你可能感兴趣的:(9. DelayQueue)