newScheduledThreadPool是如何是实现周期执行的??
ScheduledThreadPoolExecutor的执行主要分为两个部分。
- 当调用scheduleAtFixedRate() 和 scheduleWithFixedDelay() 方法时,会向ScheduledThreadPoolExecutor的DelayedWorkQueue添加一个实现了RunnableScheduledFuture接口的ScheduledFutureTask.
- 线程池中的线程从DelayedWorkQueue中获取ScheduledFutrueTask,然后执行。
源码:
//测试 main方法
public statis void main(){
Executors.newScheduledThreadPool(3).scheduleWithFixedDelay(t, 10,20,TimeUnit.SECONDS);
}
执行scheduleWithFixedDelay方法
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t); // 将任务添加到 DelayedWorkQueue中
return t;
}
获取DelayedWorkQueue中的任务
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheduledFuture<?> first = queue[0];
if (first == null)
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
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 && queue[0] != null)
available.signal();
lock.unlock();
}
}
执行任务
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime(); // 修改time变量为下次将要被执行的时间
reExecutePeriodic(outerTask);
}
}
放回到对列中去
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);
}
if (queue[0] == e) {
leader = null;
available.signal();
}
} finally {
lock.unlock();
}
return true;
具体执行流程
- 从DelayedWorkQueue中获取已到期的ScheduleFutureTask(DelayedWorkQueue.task())。
到期任务是指ScheduleFutureTask的time大于等于当前时间。 - 执行ScheduleFutureTask任务
- 修改ScheduleFutureTask的time变量为下次将要被执行的时间。
- 将修改time之后的ScheduleFutureTask放回DelayedWorkQueue中(DelayedWorkQueue.add())
ScheduledThreadPoolExecutor为了实现周期性的任务执行,对ThreadPoolExecutor做了什么?
1. 使用DelayedWorkQueue作为工作队列
2. 获取任务的方式不同
ScheduledThreadPoolExecutor是通过DelayedWorkQueue.task();获取任务的。
task() 和 poll()的区别,poll和take都是取元素,并且删除头部元素,区别在于poll()是非阻塞的,如果没有到期的元素则
返回null,take()是一直阻塞到返回到期的头部元素。peek():获取但不移除此队列的头部;如果此队列为空,则返回 null。与poll不同,如果队列中没有到期元素可用,则此方法返回下一个将到期的元素(如果存在一个这样的元素)
3. 执行周期任务后,增加了额外的处理,修改time变量,将修改后的任务重新放回DelayedWorkQueue中。