DelayQueue a concurrent queue is unbounded blocking delay queue, the queue of each element has an expiration time,
when acquiring an element from the queue, the queue will only expire elements. Head of the queue element is the fastest to expire elements.
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
private Thread leader = null;
private final Condition available = lock.newCondition();
public DelayQueue() {}
public DelayQueue(Collection<? extends E> c) {
this.addAll(c);
}
Operation offer
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();
}
}
Obtaining an exclusive lock code first, and then add elements to the priority queue, since q is the priority queue, so
the additional element, calling q.peek () method returns the current element is not necessarily added. If the code (2) determination
result is true, then the current element e is the first to expire, resetting the leader thread is null, which when excited
living condition variables avaliable inside a thread queue, the queue to tell it inside the element .
take operational
Retrieves and removes the delay time expired queue inside the element, if there is no queue waiting for the expiration element
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
//1 获取但不移除队首元素
E first = q.peek();
if (first == null)
//2 阻塞 进入等待
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
//3 时间小于=于0
if (delay <= 0)
return q.poll();
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
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();
}
}
First, acquire an exclusive lock lock. A thread queue is assumed to take the first method call queue
empty, is performed after the first == null code is 1, it executes the code. The current thread into the available Article
pieces blocked waiting queue.
When another thread B executed offer (item) element is added to the queue and method, assuming that no
other thread performs enqueue operation, element B is added to the thread elements of the first team, is executed q.peek ()
A thread is withdrawn and re-live the team get the first element of the cycle, this time the first thread B is the new element, can
know this first time is not null, is invoked first. GetDelay (TimeUnit.NANOSECONDS) way to see the element
also remaining how much time will expire, if the de lay <= O then the period has elapsed, then direct the team to return. Otherwise, check
to see if the leader is null, is not null then the other threads are executed take, put the thread into the condition queue.
If this time the leader is null, then: izt take the current thread A is the leader thread, and then execute the code (5) wait for delay
time - between (during which the thread releases the lock so other threads can offer additional element can also take blocking own)
The remaining time to expiration, the thread A competition will be re-acquire the lock, then reset leader thread nu ll, re-enter the circulation
loop, this time the team will find elements of the head has been through, it will directly return the head elements.
Before returning finally block will execute the code inside (7), Code (7) the execution result is true then the current line
after the process to remove expired elements from the queue, there are other threads into the operating team, this time calling conditions variable
singa l method, the activation condition waiting queue inside thread.
poll operation
Retrieves and removes the head-of expired elements, if not expired element null is returned
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
//如果队列为空,或者不为空但是队头元素没有过期则返回 null
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return q.poll();
} finally {
lock.unlock();
}
}
size operation
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.size();
} finally {
lock.unlock();
}
}
First, acquire an exclusive lock, and then call priority queue size method
demo
package com.ghgcn.thread.lesson07.concurrentqueue;
import java.util.Random;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
/**
* @author 刘楠
* @since 2019/6/5
*/
public class DelayQueueTest01 {
static class DelayedEle implements Delayed{
private final long delayTime ; //延迟时间
private final long expire ; //到期时间
private String taskName ; //任务名称
public DelayedEle(long delay, String taskName) {
this.delayTime = delay;
this.taskName = taskName;
this.expire = System.currentTimeMillis() +delay;
}
/**
* 剩余时间=到期时间 - 当前时间
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert( expire-System.currentTimeMillis(),TimeUnit.MILLISECONDS);
}
/**
* 优先级队列里面的优先级规则
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS)- o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public String toString() {
return "DelayedEle{" +
"delayTime=" + delayTime +
", expire=" + expire +
", taskName='" + taskName + '\'' +
'}';
}
}
public static void main(String[] args) {
//创建队列
DelayQueue<DelayedEle> queue =new DelayQueue<DelayedEle>();
ThreadLocalRandom random =ThreadLocalRandom.current();
for(int i = 0; i<10; i++){
DelayedEle ele = new DelayedEle(random.nextInt(1000),"task: "+i);
queue.offer(ele);
}
//取出
DelayedEle delayedEle=null;
for(;;){
try {
while ((delayedEle=queue.take())!=null){
System.err.println(delayedEle.toString());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
- result
DelayedEle {delayTime = 92, expire = 1559727104677, taskName = 'task: 8'}
DelayedEle {delayTime = 126, expire = 1559727104711, taskName = 'task: 3'}
DelayedEle {delayTime = 363, expire = 1559727104948, taskName = 'task 7 '}
DelayedEle {delayTime = 461, expire = 1559727105044, taskName =' task: 1 '}
DelayedEle {delayTime = 583, expire = 1559727105168, taskName =' task: 5 '}
DelayedEle {delayTime = 700, expire = 1559727105285, taskName = 'task: 4'}
DelayedEle {delayTime = 826, expire = 1559727105409, taskName = 'task: 0'}
DelayedEle {delayTime = 846, expire = 1559727105431, taskName = 'task: 9'}
DelayedEle {delayTime = 983, expire = 1559727105568, taskName = 'task: 6'}
DelayedEle delayTime = {999, expire = 1559727105584, taskName = 'task: 2'}
Create a task DelayedEle delay class, which represents the current task requires delayTime extended
much later than ms time expired, expire ms is the value of the current time plus the value of delayTime. In addition, to achieve the
Delayed interface enables a long getDelay (TimeUnit unit) is used to retrieve the current element is left much time expired, to achieve a relatively regular int compareTo (Delayed o) method is used to determine the priority queue elements.
DelayQueue PriorityQueue queue using its internal data storage, so
to achieve synchronization thread ReentrantLock. Further queue inside the elements to implement Delayed interfaces, one of which is
to get the current element to the interface to the expiration time remaining time, determines when dequeuing the element is expired, a is an element of
comparison between the interface because it is a priority the queue.