DelayQueue源码学习
DelayQueue是什么
DelayQueue实现了BlockingQueue,是一种无界队列,用来存放实现了Delayed的对象,不能存放null值,它是线程安全的。
DelayQueue是一种有序队列,对象存放顺序按照该队列存放对象的compareTo方法确定。
成员变量和构造方法
成员变量
private final transient ReentrantLock lock = new ReentrantLock();
private final Condition available = lock.newCondition();
private final PriorityQueue<E> q = new PriorityQueue<E>();
private Thread leader = null;
lock变量是可重入锁,用来保证DelayQueue的线程安全。
available是Condition对象,用来与lock进行配合,完成线程阻塞唤醒等工作。作用等同wait(),notify()。
p是一个优先级队列对象,该队列中实际存放对象的容器, 对DelayQueue的添加提取操作的实际操作对象。
leader是当前对该队列进行占用的线程(阻塞中),线程占用的时候会将此字段设置为正在占用的线程, 线程占用完毕将此字段置为null
构造方法
public DelayQueue() {}
public DelayQueue(Collection<? extends E> c) {
this.addAll(c);
}
有参构造调用父类的addAll()方法,将集合中的元素存入队列中。
主要方法
入队列 add() offer() put()
public boolean add(E e) {
return offer(e);
}
public void put(E e) {
offer(e);
}
public boolean offer(E e, long timeout, TimeUnit unit) {
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();
}
}
add,put,方法最终调用的都是offer方法,所以这里只分析offer方法。
首先加锁保证线程安全,再调用 PriorityQueue 中的offer方法(如下代码)添加数据。
/**
* PriorityQueue 中的方法
*/
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
// 扩容机制
if (i >= queue.length)
grow(i + 1);
size = i + 1;
if (i == 0)
// 没有数据直接添加
queue[0] = e;
else
// size不为空时,进行排序
siftUp(i, e);
return true;
}
//排序方法
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
// 由于DelayQueue初始化comparator为null
siftUpComparable(k, x);
}
// 通过队列中存储元素的compareTo方法进行比较 这里是任务的开始执行时间从早->晚排序
@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
出队列 take() poll()
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();
}
}
// 延迟执行poll方法
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;
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 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 <= 0)
return q.poll();
first = null;
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();
}
}
以上的poll方法和take方法都是移除并返回第一个元素,区别是take方法当队列为空时候会阻塞。