ScheduleThreadPoolExecutor源码分析(二)
DelayedWorkQueue:
DelayedWorkQueue实现了BlockingQueue接口,因此其可以作为线程池的任务队列。BlockingQueue的主要属性有以下几个:
- private RunnableScheduledFuture[] queue:作为最小堆的实现数组,主要存储ScheduledFutureTask任务列表。
- private final ReentrantLock lock:可重入锁,保证队列数据多线程下的一致性。
- private int size = 0:队列中任务总数
- private Thread leader = null:主要是为了尽量减少队列头任务执行的时间,这个字段将在tack()函数中具体讲解其用法。
- private final Condition available = lock.newCondition():锁条件变量。
DelayedWorkQueue最主要的两个方法就是take()和offer()方法。offer()方法是向队列里新增一个任务,take是从延迟队列中取出当前需要执行的任务。
public boolean offer(Runnable x) {
if (x == null)
throw new NullPointerException();
RunnableScheduledFuture e = (RunnableScheduledFuture)x;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = size;
if (i >= queue.length)
//队列满时,扩容
grow();
size = i + 1;
if (i == 0) {
queue[0] = e;
setIndex(e, 0);
} else {
//将新加入的任务按照最小堆排序规则进行排序
siftUp(i, e);
}
//当队列只有一个元素时,将leader置为null,并发送消息唤醒take或offer操作
if (queue[0] == e) {
leader = null;
available.signal();
}
} finally {
lock.unlock();
}
return true;
}
上述为offer操作,用于向队列中插入任务,所有插入的准则都是按照最小堆准则,保证堆顶元素为最先需要执行的任务。
public RunnableScheduledFuture take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
//获取堆顶元素,也就是需要现在执行的任务
RunnableScheduledFuture first = queue[0];
if (first == null)
//如果队列中没有元素则等待,对应offer()中的available.signal();
available.await();
else {
long delay = first.getDelay(TimeUnit.NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
else if (leader != null)
//如果leader不为null说明该线程正在执行其他操作(例如offer)
available.await();
else {
Thread thisThread = Thread.currentThread();
//将leader置为当前线程
leader = thisThread;
try {
//等待直到该线程需要执行的时间点,在下一个循环中获取当前任务
available.awaitNanos(delay);
} finally {
//无论什么操作,在返回前必须将leader置为null
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}