概述
ScheduledThreadPoolExecutor 常常用来作为延迟任务,或者是周期性执行某个任务
延迟任务简单的使用示如下,如果需要周期性执行任务,使用 scheduleAtFixedRate()
public static void main(String[] args) throws ClassNotFoundException, ExecutionException, InterruptedException {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
scheduledThreadPoolExecutor.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
},3,TimeUnit.SECONDS);
scheduledThreadPoolExecutor.shutdown();
}
构造方法
首先来关注构造方法
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
其实ScheduledThreadPoolExecutor的本质依然是一个线程池,但是于其他的线程池不同的是使用了 DelayedWorkQueue()
schedule()
public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
在schedule 中将 command(具体任务)delay 时间,unit(时间单位)封装成一个 ScheduledFutureTask
然后执行 delayedExecute(t)
private void delayedExecute(RunnableScheduledFuture<?> task) {
//判断线程池是否关闭
if (isShutdown())
reject(task);//拒绝策略
else {
//将这个任务添加到任务队列
super.getQueue().add(task);
//判断线程池是否关闭,这部分不考虑
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
ensurePrestart();
}
}
进入ensurePrestart();
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
线程数小于 corePoolSize ,在addWorker就会new 一个Worker (也就是新建一个线程),反之不会创建
最后在线程池获取任务的时候就会根据DelayedWorkQueue 进行 获取任务
其实这已经是ThreadPoolExecutor的内容了,感兴趣的朋友可以阅读笔者另外一篇博客,连下如下
https://blog.csdn.net/kznsbs/article/details/111958345
DelayedWorkQueue()
说到DelayedWorkQueue 相信大家很容易就想到了DelayQueue,其实DelayedWorkQueue的原理跟DelayQueue也是类似的
简单的看一下这个DelayedWorkQueue 的take()和 put()
put()
public void put(Runnable e) {
offer(e);
}
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 表示存储的元素个数
size = i + 1;
if (i == 0) {//如果是第一个直接赋值到下标0的位置
queue[0] = e;
setIndex(e, 0);
} else {//说明数组里有其他的元素,通过getDelay()的大小进行比较,将执行时间短的任务排在最前面
siftUp(i, e);
}
if (queue[0] == e) {
leader = null;
available.signal();
}
} finally {
lock.unlock();
}
return true;
}
take()
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
//上锁
lock.lockInterruptibly();
try {
for (;;) {
//前面我们看到队列里面的添加的都是RunnableScheduledFuture的子类ScheduledFutureTask
RunnableScheduledFuture<?> first = queue[0];//取第一个元素,因为在put的时候已经排好序了,所以直接取第一个就好了
if (first == null)//如果一个元素都没有就进行阻塞
available.await();
else {
//获取离执行还差多长时间
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)//到了执行时间
//就会将这个任务(ScheduledFutureTask)返回出去,但是这里买呢还会进行堆的调整,因为在take的时候永远只会取第一个元素
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();
reExecutePeriodic(outerTask);
}
}
周期执行和非周期执行最关键的部分就在ScheduledThreadPoolExecutor 的run方法里面了,其实关于具体的实现相信没有看过源码也很容易想到,周期执行就是执行完后不做任何操作嘛,对于周期执行,那么就会在执行完成后,再将这个任务重新放入任务队列