数据库连接池核心类ThreadPoolExecutor
线程池的设计类似于生产者–消费者模型。生产者主要用于添加任务到线程池,消费者从线程队列中取出线程执行任务队列中的任务。
我们知道,创建线程需要发出系统调用,陷入内核,调用内核 API 创建线程,为线程分配资源等,这一些操作有很大的开销。在高并发大流量的情况下,频繁的创建和销毁线程会大大拖慢响应速度。为了避免线程的创建和销毁,要把线程复用
起来。
Executor 框架
先看一下java中Executor框架的流程图。
Executor 接口
Executor 是 java.util.concurrent 的顶级接口,它里面只定义了一个方法execute,负责接收提交的任务。不过execute方法只能接收实现Runnable接口的实现类,对于实现Callable 接口的类,需要它的子类方法sumbmit添加。
Executor 的设计初衷就是将任务提交
和任务执行细节
进行解藕。
ExecutorService
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
可以发现,ExecutorService 也是一个接口,并且继承Executor接口。扩展了不少功能。
- shutdown 方法调用后,ExecutorService 会有序关闭正在执行的任务,但是不接受新任务。如果任务已经关闭,那么这个方法不会产生任何影响。
- shutdownNow 会尝试停止关闭所有正在执行的任务,停止正在等待的任务,并返回正在等待执行的任务列表。
- isShutdown 方法表示执行器是否已经关闭,如果已经关闭,返回 true,否则返回 false。
- isTerminated 方法表示判断所有任务再关闭后是否已完成,如果完成返回 false。这个需要注意一点,除非首先调用 shutdown 或者 shutdownNow 方法,否则 isTerminated 方法永远不会为 true。
- awaitTermination 方法会阻塞,直到发出调用 shutdown 请求后所有的任务已经完成执行后才会解除。
- submit方法支持实现Runnable接口和Callable 接口的实现类。
- invokeAll 方法用于执行给定的任务集合,执行完成后会返回一个任务列表,任务列表每一项是一个任务,每个任务会包括任务状态和执行结果,同样 invokeAll 方法也会返回 Future 对象。
- invokeAny 会获得最先完成任务的结果,即Callable 接口中的 call 的返回值,在获得结果时,会中断其他正在执行的任务,具有阻塞性。
AbstractExecutorService
AbstractExecutorService 是一个抽象类,它实现了 ExecutorService 中的部分方法。AbstractExecutorService 这个抽象类主要实现了 invokeAll 和 invokeAny 方法
ScheduledExecutorService
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
- ScheduledExecutorService 也是一个接口,它扩展了 ExecutorService 接口,提供了 ExecutorService 接口所没有
定时执行
的功能。 - schedule 方法能够延迟一定时间后执行任务,并且只能执行一次。schedule 方法也返回了一个 ScheduledFuture 对象,ScheduledFuture 对象扩展了 Future 和 Delayed 接口,它表示异步延迟计算的结果。schedule 方法支持零延迟和负延迟,这两类值都被视为立即执行任务。
- scheduleAtFixedRate 表示任务会根据固定的速率在时间 initialDelay 后不断地执行。
- scheduleWithFixedDelay表示的是以固定延迟时间的方式来执行任务。
scheduleAtFixedRate 与 scheduleWithFixedDelay的区别:
(1)scheduleAtFixedRate 是以恒定的速率来执行任务的。
(2)scheduleWithFixedDelay 是以固定时延来执行的。(固定时间) + delay(time spend)。
假设每1s执行一次。
scheduleAtFixedRate 基本都是1s左右执行一次任务。
scheduleWithFixedDelay 如果任务执行完耗时2s,那么下一次执行时间为 1 +2 左右。
ThreadPoolExecutor与 ScheduledThreadPoolExecutor
这两个是线程池的落地实现。下面是ThreadPoolExecutor的执行流程图梳理。
ThreadPoolExecutor的设计
ThreadPoolExecutor通过一个原子性操作类ctl
,保存了线程池的生命周期。
ctl是AtomicInteger
类型。它里面保存了线程池的生命周期和有效线程数量。代码中实现如下:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//Integer.SIZE = 32, COUNT_BITS = 29
private static final int COUNT_BITS = Integer.SIZE - 3;
//
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
我是这么理解的。取int的高三位。
(1)000。表示状态SHUTDOWN
(2)001。表示状态STOP
(3)010。表示状态TIDYING
(4)011。表示状态TERMINATED
(5)101。表示状态RUNNING
- RUNNING。线程池处于RUNNING状态,能接收新任务,也能处理正在运行的任务。线程池已创建出来就处于RUNNING状态。
- SHUTDOWN。线程池调用shutdown方法后,线程池会从running–>shutdown。这时候,线程池能够处理正在运行的任务,但是不会接收新的任务。
- STOP。和shutdown类似,调用shutdownnow方法时,线程池会从running–>stop。这时候不接受新任务,不处理已添加到任务队列中的任务,中断正在执行的任务。
- TIDYING。线程池处于shutdown或者stop状态时,如果线程池中的线程数量为空时,会转换为TIDYING状态。转换为TIDYING的线程池会调用terminated这个钩子函数。
- TERMINATED。线程池处于TYDYING状态,执行完terminated方法后,状态转换为TERMINATED。此时线程池彻底结束。
执行流程:
线程都保存再HashSet类型的集合中。对于创建线程的逻辑,都需要先获取mainLock锁,保证并发操作线程安全。
private final ReentrantLock mainLock = new ReentrantLock();
/**
* Set containing all worker threads in pool. Accessed only when
* holding mainLock.
*/
private final HashSet<Worker> workers = new HashSet<Worker>();
/**
* Wait condition to support awaitTermination
*/
private final Condition termination = mainLock.newCondition();
源码整体解析如下
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// 获取 ctl 的值
int c = ctl.get();
// 判断 ctl 的值是否小于核心线程池的数量
if (workerCountOf(c) < corePoolSize) {
// 如果小于,增加工作队列,command 就是一个个的任务
if (addWorker(command, true))
// 线程创建成功,直接返回
return;
// 线程添加不成功,需要再次判断,每需要一次判断都会获取 ctl 的值
c = ctl.get();
}
// 如果线程池处于运行状态并且能够成功的放入阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
// 再次进行检查
int recheck = ctl.get();
// 如果不是运行态并且成功的从阻塞队列中删除
if (! isRunning(recheck) && remove(command))
// 执行拒绝策略
reject(command);
// worker 线程数量是否为 0
else if (workerCountOf(recheck) == 0)
// 增加工作线程
addWorker(null, false);
}
// 如果不能增加工作线程的数量,就会直接执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
addWorker的源码解析。
private boolean addWorker(Runnable firstTask, boolean core) {
// retry 的用法相当于 goto
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 仅在必要时检查队列是否为空。
// 线程池状态有五种,state 越小越是运行状态
// rs >= SHUTDOWN,表示此时线程池状态可能是 SHUTDOWN、STOP、TIDYING、TERMINATED
// 默认 rs >= SHUTDOWN,如果 rs = SHUTDOWN,直接返回 false
// 默认 rs < SHUTDOWN,是 RUNNING,如果任务不是空,返回 false
// 默认 RUNNING,任务是空,如果工作队列为空,返回 false
//
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 执行循环
for (;;) {
// 统计工作线程数量
int wc = workerCountOf(c);
// 如果 worker 数量>线程池最大上限 CAPACITY(即使用int低29位可以容纳的最大值)
// 或者 worker数量 > corePoolSize 或 worker数量>maximumPoolSize ),即已经超过了给定的边界
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 使用 CAS 增加 worker 数量,增加成功,跳出循环。
if (compareAndIncrementWorkerCount(c))
break retry;
// 检查 ctl
c = ctl.get(); // Re-read ctl
// 如果状态不等于之前获取的 state,跳出内层循环,继续去外层循环判断
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
/*
worker数量+1成功的后续操作
* 添加到 workers Set 集合,并启动 worker 线程
*/
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 包装 Runnable 对象
// 设置 firstTask 的值为 -1
// 赋值给当前任务
// 使用 worker 自身这个 runnable,调用 ThreadFactory 创建一个线程,并设置给worker的成员变量thread
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 在持有锁的时候重新检查
// 如果 ThreadFactory 失败或在获得锁之前关闭,请回退。
int rs = runStateOf(ctl.get());
//如果线程池在运行 running<shutdown 或者 线程池已经 shutdown,且firstTask==null
// (可能是 workQueue 中仍有未执行完成的任务,创建没有初始任务的 worker 线程执行)
//worker 数量 -1 的操作在 addWorkerFailed()
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// workers 就是一个 HashSet 集合
workers.add(w);
// 设置最大的池大小 largestPoolSize,workerAdded 设置为true
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
//如果启动线程失败
// worker 数量 -1
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// 允许打断
// new Worker() 是 state==-1,此处是调用 Worker 类的 tryRelease() 方法,
// 将 state 置为0
w.unlock();
boolean completedAbruptly = true;
try {
// 调用 getTask() 获取任务
while (task != null || (task = getTask()) != null) {
// 获取全局锁
w.lock();
// 确保只有在线程 STOPING 时,才会被设置中断标志,否则清除中断标志。
// 如果一开始判断线程池状态 < STOPING,但 Thread.interrupted() 为 true,
// 即线程已经被中断,又清除了中断标示,再次判断线程池状态是否 >= stop
// 是,再次设置中断标示,wt.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,完成任务数 + 1,并进行解锁
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
// 最后处理 worker 的退出
} finally {
processWorkerExit(w, completedAbruptly);
}
}
private Runnable getTask() {
// 判断最后一个 poll 是否超时。
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.
// 必要时检查队列是否为空
// 对线程池状态的判断,两种情况会 workerCount-1,并且返回 null
// 线程池状态为 shutdown,且 workQueue 为空(反映了 shutdown 状态的线程池还是要执行 workQueue 中剩余的任务的)
// 线程池状态为 stop(shutdownNow() 会导致变成 STOP)(此时不用考虑 workQueue 的情况)
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
// 是否需要定时从 workQueue 中获取
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 如果工作线程的数量大于 maximumPoolSize 会进行线程剔除
// 如果使用了 allowCoreThreadTimeOut ,并且工作线程不为0或者队列有任务的话,会直接进行线程剔除
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;
}
}
}
如何实现线程复用呢?
我的理解是线程对象work,在从任务队列中获取任务时,如果线程数量 > 核心线程数量。通过workquue.poll(keepAliveTime)方法,设置超时时间获取任务。超时时间内没有返回任务,该线程work进行清除操作线程集合中移除线程work。如果线程数量 < 核心线程数,通过workQueue.take()阻塞等待获取任务。
使用线程池,如果获取执行返回结果
我们可以看到线程池的submit方法,都有返回Future
接口。
Future接口有5个方法。
- cancel
取消任务 - isCannel
判断任务是否已取消 - isDone
判断任务是否结束 - get
获取执行结果 - get(timeout ,unit)
设置超时时间获取执行结果
FutureTask实现了Future 和 Runnable接口。本身可以作为任务交给ThreadPooExecutor去执行。然后通过FutrueTask获取执行结果。