常用方法
在上节中我们知道,任务最终的执行与Worker类有关,线程池中有多少个Worker就代表有多少个线程。
submit(Callable<T> task)、submit(Runnable task):将任务封装成FutureTask对象,它是RunnableFuture的实现类,内含状态、结果等成员变量,如果是Callable实现类,会调用call()方法,如果是Runnable实现类,会调用run()方法。用get()将会阻塞直到出结果,对于返回类型是void的则返回null
invokeAll(Collection<? extends Callable<T>> tasks):tasks是Callable接口的实现类集合,执行实现类任务,阻塞至所有任务完成
shutdown():将线程池状态置为SHUTDOWN,停止接收新任务,然后尝试中断等待阻塞队列任务的Worker线程
ThreadPoolExecutor->interruptIdleWorkers():
for (Worker w : workers) {
Thread t = w.thread;
// w是worker类,tryLock()只有状态为0的时候才会成功
// 注意到任务未执行完毕的状态是1,所以这边就不会中断
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
......
shutdownNow():将线程池状态置为STOP,然后停止线程池中的Worker线程(无论是否完成),返回阻塞队列中的任务
ThreadPoolExecutor->interruptWorkers():
for (Worker w : workers)
w.interruptIfStarted();
getPoolSize():返回线程池中的Worker线程数量
getActiveCount():返回线程池中正在执行任务的Worker线程数量
getCompletedTaskCount():返回线程池完成任务的数量,由completedTaskCount与各Worker线程相加
prestartCoreThread():添加一个核心工作线程
prestartAllCoreThreads():添加核心工作线程,直到达到corePoolSize的数量
setCorePoolSize(int corePoolSize):将核心线程数设为corePoolSize,如果此时工作线程数大于核心线程数,尝试中断等待阻塞队列任务的Worker线程;如果扩大了核心线程数,尝试将阻塞队列中的任务添加Worker线程
setMaximumPoolSize(int maximumPoolSize):将最大线程数设为maximumPoolSize,如果此时工作线程数大于最大线程数,尝试中断等待阻塞队列任务的Worker线程
setKeepAliveTime(long time, TimeUnit unit):重新设置keepAliveTime+unit,如果此时间隔设小的话,尝试中断等待阻塞队列任务的Worker线程
特殊线程池
单例线程池
SingleThreadExecutor()一个corePoolSize,超出全部放入LinkedBlockingQueue阻塞队列
固定线程池
FixedThreadExecutor(n):n个corePoolSize,超出全部放入LinkedBlockingQueue阻塞队列
缓存线程池
CachedThreadPool:0个corePoolSize,阻塞队列SynchronousQueue不接收任务,Integer.MAX_VALUE个maximumPoolSize,60秒没获取到任务则Worker退出
定时线程池
ScheduledThreadPool(n):n个corePoolSize,先将任务放入DelayedWorkQueue阻塞队列,然后启动核心线程。返回ScheduledThreadPoolExecutor对象。
scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit):假设这里的unit是秒,initialDelay是2,period是3,那么延时2s之后启动,3s后,如果任务完成则立即执行下一次,否则等待任务完成之后执行下一次。返回ScheduledFutureTask对象。
scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit):假设这里的unit是秒,initialDelay是2,period是3,那么延时2s之后启动,任务完成3s后,执行下一次。返回ScheduledFutureTask对象。
ScheduledThreadPoolExecutor->execute():
schedule(command, 0, NANOSECONDS);
ScheduledThreadPoolExecutor->schedule():
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
// 将任务添加进阻塞队列,如果工作线程数小于核心线程数,那么启动一个工作线程
delayedExecute(t);
ScheduledThreadPoolExecutor->scheduleAtFixedRate():
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
ScheduledThreadPoolExecutor->scheduleWithFixedDelay():
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
triggerTime(delay, unit),计算出将要执行任务的纳秒时间,如果是0,那么就是当前的纳秒时间;如果delay是3,unit是秒,那么就是当前时间+3s的纳秒时间。
ScheduledThreadPoolExecutor->ScheduledFutureTask->ScheduledFutureTask(Runnable r, V result, long ns, long period):
super(r, result);
// 执行任务的纳秒时间
this.time = ns;
// 时间周期
this.period = period;
this.sequenceNumber = sequencer.getAndIncrement();
注意到三种方法指定了不同的时间周期,其中execute()是0,scheduleAtFixedRate()是一个正数,scheduleWithFixedDelay()是一个负数。
DelayedWorkQueue重写阻塞队列常用方法,以take()为例
ScheduledThreadPoolExecutor->DelayedWorkQueue->take():
RunnableScheduledFuture<?> first = queue[0];
if (first == null)
// 如果没有任务,就一直阻塞
// 调用add()添加任务时,会调用available.signal(),这里就会唤醒
available.await();
else {
// 当前时间与执行任务时间的差值
long delay = first.getDelay(NANOSECONDS);
// 差值小于0,说明已经过了任务时间,需要执行任务
if (delay <= 0)
return finishPoll(first);
first = null;
......
// 否则,假如相差了一秒,那就让线程休眠一秒后再尝试
available.awaitNanos(delay);
任务完成后,会根据不同的方法计算出下一次执行的时间。
ScheduledThreadPoolExecutor->ScheduledFutureTask->run():
// 周期是否等于0
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
// 如果等于0,就普通的执行
else if (!periodic)
ScheduledFutureTask.super.run();
// 执行任务,若任务状态不为0则返回false
else if (ScheduledFutureTask.super.runAndReset()) {
// 设置下一次执行任务的时间
setNextRunTime(); // --1
// 再次放入阻塞队列
reExecutePeriodic(outerTask);
}
ScheduledThreadPoolExecutor->ScheduledFutureTask->setNextRunTime(): // --1
long p = period;
if (p > 0)
// 直接计算出下一次的执行时间,对应的是scheduleAtFixedRate()方法
time += p;
else
// 下一次的执行时间=当前时间+时间周期,对应的是scheduleWithFixedDelay()方法
time = triggerTime(-p);
scheduledFuture.cancel(boolean mayInterruptIfRunning):取消任务,若mayInterruptIfRunning为true则中断线程。原理是改变任务状态,当状态不为0的时候,直接跳过执行任务和计算下一次任务执行时间的步骤。
应用:SpringBoot中定时任务注解@EnableScheduling的实现