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
核心要点
- 使用此队列时,元素必须要实现
Delayed
接口 - 当已经有一个线程等待获取队列头元素时,其他也想要获取元素的线程就会进行等待阻塞状态
- 迭代器不和内部的优先级队列保持一致性
- 迭代器的
remove()
方法与内部的优先级队列保持一致性