概述
线程池是什么不用多说,我们先来看一下各种线程池继承关系,鸟瞰整个全貌
先看看顶层接口
public interface Executor {
# 只有一个执行方式
void execute(Runnable command);
}
复制代码
ExecutorService 在 Executor 基础上增加了一个 submit方法,可以用于传入 Callable接口的数据
public interface ExecutorService extends Executor {
<T> Future<T> submit(Callable<T> task);
}
复制代码
接下来我们看看实现类的源码逻辑
AbstractExecutorService
public abstract class AbstractExecutorService implements ExecutorService {
# 将 callable包装成futuretask
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
# 包装成futuretask然后交给子类执行
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
}
复制代码
ThreadPoolExecutor
ThreadPoolExecutor 最核心的思想就是一堆worker在阻塞队列里面拉任务执行,就这么简单 我们来看看源码,首先看看 内部类
Worker
# worker 继承与 AQS,是一把独占锁,并且实现了 Runnable,可以被放入线程中执行
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
# 运行当前worker的线程
final Thread thread;
# 初始化执行的任务
Runnable firstTask;
# 完成的任务数
volatile long completedTasks;
# 创建worker
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
# 这一行很重要,将自身 runnable 和 thread绑定在了一起
this.thread = getThreadFactory().newThread(this);
}
# 当 thread 启动的时候 这个run方法就会开启
public void run() {
runWorker(this);
}
# AQS 的获取释放资源,可以看出是独占锁
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;
}
}
复制代码
看的出 Worker 是一个 AQS的锁,并且还是一个runnable,自身绑定了一个线程,当线程开启的时候,自己就会执行 run方法。那么线程什么时候开启呢,继续往下看源码. 先看几个关键的属性,ctl
ThreadPoolExecutor 用了 int 32 位中前3位来表示线程池的状态,后面29位表示线程池的worker数量。 判断的时候都是用位运算来操作
public class ThreadPoolExecutor extends AbstractExecutorService {
# ThreadPoolExecutor 用了 int 32 位中前3位来表示线程池的状态,后面29位表示线程池的worker数量
# 判断的时候都是用位运算来操作
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
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; }
# 新建线程池传入的阻塞队列
private final BlockingQueue<Runnable> workQueue;
# Worker集合
private final HashSet<Worker> workers = new HashSet<Worker>();
复制代码
然后这就是经典的线程池提交任务逻辑. 核心线程数没满创建线程,满了放队列,队列满了创建最大线程数,再满了走拒绝策略
# 线程池提交任务方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
# 获得状态值
int c = ctl.get();
# 通过位运算算出来当前worker数量是不是小于核心线程数
if (workerCountOf(c) < corePoolSize) {
# 如果是直接创建worker线程返回
if (addWorker(command, true))
return;
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:
for (;;) {
# 拿出状态
int c = ctl.get();
int rs = runStateOf(c);
# 判断线程池状态,如果不符合就返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
# 拿到当前线程数
int wc = workerCountOf(c);
# 如果当前线程数已经超过核心或者最大线程数了,就创建worker失败
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
# 可以继续创建线程,把状态加一
if (compareAndIncrementWorkerCount(c))
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 {
# 创建worker对象,记住worker是一个锁,还是一个runnable,里面有一个thread,执行了start方法就是执行worker里面的run方法
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
# 再次判断线程池状态
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
# 如果worker里面的线程被别人start了抛出异常
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
# 线程池中添加worker
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
# 如果worker放入线程池当中了,开启worker里面的线程,worker就会执行run方法
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
复制代码
可以看到,方法和核心逻辑就是 创建了一个Worker对象放到线程池中,然后开启一个线程执行Worker里面的方法. 所以接下来就看看Worker里面的run方法到底在做些什么.
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
# 别的不用管,就看这一句,task = getTask().
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
# 执行 task这个runnable 的run方法
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);
}
}
# 从阻塞队列中获取任务
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
# 又是一堆状态判断不用看
int c = ctl.get();
int rs = runStateOf(c);
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
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;
}
}
}
复制代码
至此整个 ThreadPoolExecutor的逻辑就清楚了,一个worker当被添加到线程池中的时候会开启一个线程,那个线程就等着从 阻塞队列中take数据,获得到任务之后就执行,执行完毕继续在队列中等待。
ScheduledThreadPoolExecutor
先来看看基本使用, ScheduledThreadPoolExecutor 分为可以延迟运行一次的任务,和定时执行的任务
public static void main(String[] args) throws Exception {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
# 定时执行任务
executor.scheduleAtFixedRate(()->{
System.out.println("haha");
},0,2,TimeUnit.SECONDS);
# 只执行一次的任务
executor.schedule(()->{
System.out.println("fsfsf");
},5,TimeUnit.SECONDS);
System.in.read();
}
复制代码
用法很简单,需要注意的是,任务中的延时时间是根据开始任务的时间来算的,所以加入延时2秒执行,任务执行了3秒,那么执行完毕后,因为已经超过2秒了,就会马上执行任务,而不会再等待2秒。 先来看看提交任务的代码, 其实就是创建一个 ScheduledFutureTask
丢到队列中等待消费
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
# 把任务包装成了一个 ScheduledFutureTask
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
# 将任务放到队列中
delayedExecute(t);
return 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();
}
}
复制代码
有了 ThreadPoolExecutor 的前置知识,我们知道 worker会不断的监听队列里面的任务,然后拿下来执行任务。那么接下来我们就应该看看这个 ScheduledFutureTask
中的 run方法里面逻辑
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> {
public void run() {
# 判断是不是定时执行,在初始化的时候设置
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
# 如果不是定时执行,执行一次就结束了
ScheduledFutureTask.super.run();
else {
# 执行定时任务,这里的执行不会修改 FutureTask的状态
if(ScheduledFutureTask.super.runAndReset()){
# 设置下次执行的时间
setNextRunTime();
# 重新把任务放到队列里面去
reExecutePeriodic(outerTask);
}
}
}
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
if (canRunInCurrentRunState(true)) {
# 找到队列把任务重新丢进去
super.getQueue().add(task);
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
ensurePrestart();
}
}
}
复制代码
核心思想就是worker执行完任务之后重新丢回队列的这么一个逻辑