一、线程池基础
1 引子
随着当今计算机行业飞速发展,硬件性能日新月异,摩尔定律逐渐失效,多核CPU成为了当今主流。使用多线程并行计算提高服务性能成为了程序人员需掌握的基础,java.util.concurrent 为我们提供了线程池工具类 ThreadPoolExecutor 类,帮助开发人员更好更方便的创建和使用线程池。
2 线程池定义
线程池是一种类似于 Mysql 的池化思想管理工具。Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。
3 为什么使用线程池(好处)
- 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立 即执行。假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线 程中执行任务的时间,T3 销毁线程时间。 如果:T1 + T3 远大于 T2,则可以 采用线程池,以提高服务器性能。线程池技术正是关注如何缩短或调整 T1,T3 时 间的技术,从而提高服务器程序性能的。它把 T1,T3 分别安排在服务器程序的 启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时, 不会有 T1,T3 的开销了。
- 提高线程的可管理性。 线程是稀缺资源,如果无限制地创建,不仅会 消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和 监控。
4 线程池解决的问题
线程池需要解决的核心问题是资源管理问题。在并发环境下,系统不能确定在程序运行过程中有多少任务要执行,需要使用多少资源。这些种种会带来以下问题:
- 频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
- 对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
- 系统无法合理管理内部的资源分布,会降低系统的稳定性。
为解决资源分配问题,线程池采用了 “池化”(Pooling)思想。池化,顾名思义,是为了最大化收益并最小化风险,而将资源统一在一起管理的一种思想。
5 ThreadPoolExecutor 的类关系
Executor 是一个接口,它是 Executor 框架的基础,它将任务的提交与任务的 执行分离开来;
ExecutorService 接口继承了 Executor,在其上做了一些 shutdown()、submit() 的扩展,可以说是真正的线程池接口; AbstractExecutorService 抽象类实现了 ExecutorService 接口中的大部分方法;
ThreadPoolExecutor 是线程池的核心实现类,用来执行被提交的任务。
ScheduledExecutorService 接口继承了 ExecutorService 接口,提供了带"周期 执行"功能 ExecutorService; ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行命令, 或者定期执行命令。
ScheduledThreadPoolExecutor 比 Timer 更灵活,功能更强大。
6 线程池的创建各个参数含义
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
corePoolSize 线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于 corePoolSize,如果当前线程数为 corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行; 如果执行了线程池的 prestartAllCoreThreads() 方法,线程池会提前创建并启动所有核心线程。
maximumPoolSize 线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于 maximumPoolSize。
keepAliveTime 线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间。默认情况下,该参数只在线程数大于 corePoolSize 时才有用。
TimeUnit keepAliveTime 的时间单位
workQueue workQueue 必须是 BlockingQueue 阻塞队列。当线程池中的线程数超过它的 corePoolSize 的时候,线程会进入阻塞队列进行阻塞等待。通过 workQueue,线 程池实现了阻塞功能
workQueue 用于保存等待执行的任务的阻塞队列,一般来说,我们应该尽量使用有界队列,因为使用无界队列作为工作队列会对线程池带来如下影响:
1)当线程池中的线程数达到 corePoolSize 后,新任务将在无界队列中等待, 因此线程池中的线程数不会超过 corePoolSize。
2)由于 1,使用无界队列时 maximumPoolSize 将是一个无效参数。
3)由于 1 和 2,使用无界队列时 keepAliveTime 将是一个无效参数。
4)更重要的,使用无界 queue 可能会耗尽系统资源,有界队列则有助于防止资源耗尽,同时即使使用有界队列,也要尽量控制队列的大小在一个合适的范围。
所以我们一般会使用,ArrayBlockingQueue、LinkedBlockingQueue、 SynchronousQueue、PriorityBlockingQueue。 threadFactory 创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名,当然还可以更加自由的对线程做更多的设置,比如设置所有的线程为守护线程。
RejectedExecutionHandler 线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了 4 种策略:
(1)AbortPolicy:直接抛出异常,默认策略;
(2)CallerRunsPolicy:用调用者所在的线程来执行任务;
(3)DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
(4)DiscardPolicy:直接丢弃任务; 当然也可以根据应用场景实现 RejectedExecutionHandler 接口,自定义饱和 策略,如记录日志或持久化存储不能处理的任务。
ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
corePoolSize 线程池中的核心线程数。
ThreadFactory 创建线程的工厂
RejectedExecutionHandler 线程池的饱和策略
Tips:
线程池可以扩展吗?如在任务执行前后做一些我们自己业务中的事。
实际上,JDK 的线程池已经为我们预留的接口,在线程池核心方法中,有 2 个方法是空的,就是给我们预留的。还有一个线程池退出时会调用的方法。
每个任务执行前后都会调用 beforeExecute 和 afterExecute 方法。 相当于执行了一个切面。而在调用 shutdown 方法后则会调用 terminated 方法。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
7.线程池工作机制
1)如果当前运行的线程少于 corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
2)如果运行的线程等于或多于 corePoolSize,则将任务加入 BlockingQueue。
3)如果无法将任务加入 BlockingQueue(队列已满),则创建新的线程来处 理任务。
4)如果创建新线程将使当前运行的线程超出 maximumPoolSize,任务将被拒绝,并调用 RejectedExecutionHandler.rejectedExecution()方法。
二、线程池的使用
1 ThreadPoolExecutor 使用
package com.sonny.classexercise.concurrent.threadpool;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author Xionghaijun
* @date 2022/11/14 22:18
*/
public class CustomerRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//自定义拒绝策略
executor.remove(r);
}
}
package com.sonny.classexercise.concurrent.threadpool;
import java.util.concurrent.*;
/**
* 不同线程池类型速度测试
*
* @author Xionghaijun
* @date 2022/11/14 22:16
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService1 = Executors.newCachedThreadPool();//快
ExecutorService executorService2 = Executors.newFixedThreadPool(10);//慢
ExecutorService executorService3 = Executors.newSingleThreadExecutor();//最慢
// RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
// @Override
// public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// // 写数据库的代码
// }
// };
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(10), new CustomerRejectedExecutionHandler());//自定义线程
for (int i = 1; i <= 100; i++) {
executorService1.execute(new MyTask(i));
executorService2.execute(new MyTask(i));
executorService3.execute(new MyTask(i));
threadPoolExecutor.execute(new MyTask(i));
}
}
}
/***
* 项目
*/
class MyTask implements Runnable {
int i = 0;
public MyTask(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "程序员做第" + i + "个项目");
try {
Thread.sleep(3000L);//业务逻辑
} catch (Exception e) {
e.printStackTrace();
}
}
}
2 ScheduledThreadPoolExecutor 使用
package com.sonny.classexercise.concurrent.threadpool;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 固定延时间隔线程池
*
* @author Xionghaijun
* @date 2022/11/14 22:23
*/
public class ScheduledThreadPoolExecutorExample {
public static void main(String[] args) {
ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(5);
Task task = new Task("任务");
System.out.println("Created : " + task.getName());
// executor.schedule(task, 2, TimeUnit.SECONDS);
// executor.scheduleWithFixedDelay(task, 0, 2, TimeUnit.SECONDS); //任务+延迟
executor.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS);//任延迟取最大值 稳定定时器
}
}
class Task implements Runnable {
private final String name;
public Task(String name) {
this.name = name;
}
public String getName() {
return name;
}
AtomicInteger atomicInteger = new AtomicInteger();
public void run() {
atomicInteger.incrementAndGet();
if (atomicInteger.get() > 100) {
throw new RuntimeException("失败");
}
System.out.println("Executing : " + name + ", Current Seconds : " + new Date().getSeconds());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三、源码分析
线程执行生命周期如下图:
ThreadPoolExecutor核心方法分析:
execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 1、判断当前的线程数是否小于corePoolSize如果是,使用入参任务通过addWord方法创建一个新的线程,如果能完成新线程创建exexute方法结束,成功提交任务;
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2、在第一步没有完成任务提交;状态为运行并且能成功加入任务到工作队列后,再进行一次check,如果状态在任务加入队列后变为了非运行(有可能是在执行到这里线程池shu tdown了)。然后再判断当前线程数是否为0(有可能这个时候线程数变为了0),如是,新增一个线程。
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
//判断当前工作线程池数是否为0, 如果是创建一个null任务,任务在堵塞队列存在了就会从队列中取出 这样做的意义是保证线程池在running状态必须有一个任务在执行
addWorker(null, false);
}
//3、如果不能加入任务到工作队列,将尝试使用任务新增一个线程,如果失败,则是线程池已经shutdown或者线程池已经达到饱和状态,所以reject; 从上面新增任务的execute方法也可以看出,拒绝策略不仅仅是在饱和状态下使用,在线程池进入到关闭阶段同样需要使用到;
else if (!addWorker(command, false))
reject(command);
}
- 判断当前的线程数是否小于corePoolSize如果是,使用入参任务通过addWord方法创建一个 新的线程,如果能完成新线程创建exexute方法结束,成功提交任务;
- 在第一步没有完成任务提交;状态为运行并且能成功加入任务到工作队列后,再进行一次 check,如果状态在任务加入队列后变为了非运行(有可能是在执行到这里线程池shutdown 了),非运行状态下当然是需要reject;然后再判断当前线程数是否为0(有可能这个时候线程数 变为了0),如是,新增一个线程;
- 如果不能加入任务到工作队列,将尝试使用任务新增一个线程,如果失败,则是线程池已经 shutdown或者线程池已经达到饱和状态,所以reject; 从上面新增任务的execute方法也可以看出,拒绝策略不仅仅是在饱和状态下使用,在线程池进 入到关闭阶段同样需要使用到。
addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
//goto写法 用于重试
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
//线程状态非运行并且非shutdown状态任务为空,队列非空就不能新增线程了
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
//当前线程大于等于最大值;根据所传配置判断大于等于核心线程数,最大线程数
return false;
if (compareAndIncrementWorkerCount(c))
//工作线程池数量+1 如果成功 26 就跳出死循环
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
// 进来的状态和此时的状态发生改变 重头开始 重试
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
//毎一个worker就是一个线程数
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
//重新获取当前线程状态
int rs = runStateOf(ctl.get());
//小于shutdown就是running状态
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//SHUTDOWN 和firstTask 为空是从队列中处理任务 那就可以放到集合中,线程还没start 就是alive就直接异常
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//启动线程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
//失败回退 从wokers移除w 线程数减1 尝试结束线程池
addWorkerFailed(w);
}
return workerStarted;
}
Worker方法:
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
//正在运行woker线程
final Thread thread;
/** Initial task to run. Possibly null. */
//传入的任务
Runnable firstTask;
/** Per-thread task counter */
//完成的任务数 监控用
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
//禁止线程中断
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
runwoker方法:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
//把state从‐1改为0 意思是可以允许中断
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
//如果当前线程池状态等于stop 就中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
//这设置为空 等下次循环就会从队列里面获取
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
getTask方法:
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
//timed是标志超时销毁 ,allowCoreThreadTimeOut true 核心线程池也是可以销毁的
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
processWorkerExit方法:
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
ScheduledThreadPoolExecutor核心方法分析:
delayedExecute方法:
private void delayedExecute(RunnableScheduledFuture<?> task) {
//如果线程池不是RUNNING状态,则使用拒绝策略把提交任务拒绝掉
if (isShutdown())
reject(task);
else {
//与ThreadPoolExecutor不同,这里直接把任务加入延迟队列
super.getQueue().add(task);
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
//如果当前状态无法执行任务,则取消
task.cancel(false);
else
和ThreadPoolExecutor不一样,corePoolSize没有达到会增加Worker,确保提交的任务能够被执行
ensurePrestart();
}
}
offer方法:
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)
//容量扩增50%
grow();
size = i + 1;
if (i == 0) {
queue[0] = e;
setIndex(e, 0);
} else {
// 插入堆尾。
siftUp(i, e);
}
if (queue[0] == e) {
// 如果新加入的元素成为了堆顶,则原先的leader就无效了
leader = null;
// 由于原先leader已经无效被设置为null了,这里随便唤醒一个线程(未必是原先的leader)来 取走堆顶任务。
available.signal();
}
} finally {
lock.unlock();
}
return true;
}
siftup方法:
private void siftUp(int k, RunnableScheduledFuture<?> key) {
// 找到父节点的索引
while (k > 0) {
int parent = (k - 1) >>> 1;
RunnableScheduledFuture<?> e = queue[parent];
if (key.compareTo(e) >= 0)
// 如果key节点的执行时间大于父节点的执行时间,不需要再排序了
break;
// 如果key.compareTo(e) < 0,说明key节点的执行时间小于父节点的执行时间,需要把父节点移到后面
queue[k] = e;
setIndex(e, k);
k = parent;
}
// key设置为排序后的位置中
queue[k] = key;
setIndex(key, k);
}
run方法:
public void run() {
// 是否周期性,就是判断period是否为0。
boolean periodic = isPeriodic();
// 检查任务是否可以被执行。
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
// 如果非周期性任务直接调用run运行即可。
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
// 如果成功runAndRest,则设置下次运行时间并调用reExecutePeriodic。
setNextRunTime();
// 需要重新将任务(outerTask)放到工作队列中。此方法源码会在后文介绍ScheduledThread PoolExecutor本身API时提及。
reExecutePeriodic(outerTask);
}
}
fied-rate模式和fixed-delay模式区别
private void setNextRunTime() {
long p = period;
/*
fixed‐rate模式,时间设置为上一次时间+p。
提一句,这里的时间其实只是可以被执行的最小时间,不代表到点就要执行。
如果这次任务还没执行完是肯定不会执行下一次的。
*/
if (p > 0)
/*
fixed‐delay模式,计算下一次任务可以被执行的时间。
简单来说差不多就是当前时间+delay值。因为代码走到这里任务就已经结束了,now()可以认 为就是任务结束时间。
*/
time += p;
else
time = triggerTime(-p);
}
long triggerTime(long delay) {
/*
如果delay < Long.Max_VALUE/2,则下次执行时间为当前时间+delay。
否则为了避免队列中出现由于溢出导致的排序紊乱,需要调用overflowFree来修正一下 delay(如果有必要的话)。
*/
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
/**
* 主要就是有这么一种情况:
* 某个任务的delay为负数,说明当前可以执行(其实早该执行了)。
* 工作队列中维护任务顺序是基于compareTo的,在compareTo中比较两个任务的顺序会用time 相减,负数则说明优先级高。
* 那么就有可能出现一个delay为正数,减去另一个为负数的delay,结果上溢为负数,则会导致 compareTo产生错误的结果。
* 为了特殊处理这种情况,首先判断一下队首的delay是不是负数,如果是正数不用管了,怎么 减都不会溢出。
* 否则可以拿当前delay减去队首的delay来比较看,如果不出现上溢,则整个队列都ok,排序 不会乱。
* 不然就把当前delay值给调整为Long.MAX_VALUE + 队首delay。
*/
private long overflowFree(long delay) {
Delayed head = (Delayed) super.getQueue().peek();
if (head != null) {
long headDelay = head.getDelay(NANOSECONDS);
if (headDelay < 0 && (delay - headDelay < 0))
delay = Long.MAX_VALUE + headDelay;
}
return delay;
}
循环的根据key节点与它的父节点来判断,如果key节点的执行时间小于父节点,则将两个节点 交换,使执行时间靠前的节点排列在队列的前面。 可以理解为一个树形的结构,最小点堆的结构;父节点一定小于子节点。
DelayQueue内部封装了一个PriorityQueue,它会根据time的先后时间排序(time小的排在前 面),若time相同则根据sequenceNumber排序。