线程池ThreadPoolExecutor分析
ThreadPoolExecutor是JDK线程池的核心类
线程池是用来管理线程的。用来应对正常情况下,线程执行完任务后不被销毁,继续执行其它的任务,减少创建线程,线程回收,减少线程的上下文切换成本。特殊情况下,任务突然激增,处理不过来先放入阻塞队列等待被处理,如果仍然处理不过来可以创建临时线程去处理任务,用户只需要设定上限即可
可以有异步执行的效果,比如发送短信,发送邮件,甚至使用MQ推送消息,也可以包装成任务丢到线程池中去异步处理
文章目录
简介
1,在JDK提供的线程池中,有变量记录了线程执行任务的个数,以及整个线程池执行任务的个数
2,JDK线程池也提供了相应的钩子函数用于逻辑扩展,beforeExcute和afterExcute方法
beforeExcute:在每个任务执行前执行
afterExcute:每个任务执行后执行
3,特殊情况下,任务突然激增导致线程不够用会先将任务放入阻塞队列中,阻塞队列放满时如果仍然处理不过来则创建临时线程去处理,还处理不过来,就根据拒绝策略进行处理
(也正是因为这个特征,tomcat才会重写线程池,不会等待队列满时才创建临时线程,不然可能很多io请求直接堆积在队列中,即时性很差,可能还没执行请求就超时了)
4,有submit和execute两种提交方式,前者任务被futureTask类包装一下,用来封装任务的返回值
提示:以下是本篇文章正文内容,下面案例仅供参考
一、构造方法核心参数分析
// corePoolSize: 核心线程数,执行完任务后不会被销毁,继续执行下一个任务
// 如果allowCoreThreadTimeOut配置为true,则核心线程达到过期时间后,也会被销毁,该参数默认为false
public ThreadPoolExecutor(int corePoolSize,
// 线程池内最大线程数,队列满时任务处理不过来,会创建临时线程去处理,一直创建到池内线程数为该数量为止
int maximumPoolSize,
// 临时线程的存活时间,线程空闲并且过期后临时线程会被回收
long keepAliveTime,
// 过期时间的单位
TimeUnit unit,
// 存放任务的阻塞队列,需要注意无界队列内存溢出的风险
BlockingQueue<Runnable> workQueue,
// 线程工厂,创建线程用的。线程优先级默认为5
ThreadFactory threadFactory,
// 任务拒绝策略
RejectedExecutionHandler handler) {
// 这个校验,意味着核心线程数可以配置为0
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
// 将时间转成纳秒,精度会更高一些
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
1,RejectedExecutionHandle任务拒绝策略详情:
1.1,AbortPolicy:直接抛异常:RejectedExecutionException
1.2,CallewrRunsPolicy:交给提交任务的线程去处理这个任务
1.3,DiscardIldestPolicy:丢弃掉队列中最近的任务,执行当前任务
1.4,DiscardPolicy:对任务不做处理
2,allowCoreTreadCodeTimeout:该参数为true时,空闲的核心线程达到过期时间后,也会被销毁。默认为false
二、ThreadPoolExecutor核心属性分析
// ctl的高三位代表线程池的状态
// ctl的低29位代表工作线程数的个数,最多支持5亿多个
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
// 池内线程池个数的上限
// 值:00011111 11111111 11111111 11111111 => 536870911
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 线程池的5个状态
// 高三位: 111(-536870912) 正常状态,可执行任务,可接受任务
private static final int RUNNING = -1 << COUNT_BITS;
// 高三位:000(0),执行shutDown()为该状态,不在接受新任务,将队列中的任务和正在执行的任务处理完后结束
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 高三位: 001(536870912),STOP状态,线程池即将关闭,阻塞队列的任务不在处理,正在处理任务的线程设置中断标志位
private static final int STOP = 1 << COUNT_BITS;
// 高三位:010(1073741824),是一个过度状态,由SHUTDOWN或STOP转换过来,在执行terminated后进入TERMINATED状态
private static final int TIDYING = 2 << COUNT_BITS;
// 高三位:011(1610612736),关闭状态
private static final int TERMINATED = 3 << COUNT_BITS;
// 以下方法入参是ctl
// 拿到高三位的值:
// 对CAPACITY进行取反: 00011111 11111111 11111111 11111111 => 11100000 00000000 00000000 00000000
// 基于&运算,获取高三位的值:11100000 00000000 00000000 00000000 => 111
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 计算工作线程数,根据CAPACITY计算出低29位的值
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
/**
* Tracks largest attained pool size. Accessed only under
* mainLock.
*/
private int largestPoolSize;
// 任务完成数
private long completedTaskCount;
状态库流转图:
三、基本使用
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
}, new ThreadPoolExecutor.AbortPolicy());
// 模拟100个任务
for (int i = 0; i < 100; i++) {
// 不能调用get方法,因为是阻塞方法,会等到任务执行完毕
threadPoolExecutor.submit(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:" + Thread.currentThread().getName());
});
}
}
四、ThreadPoolExecutor核心方法分析
1.submit方法分析
代码如下:
// 带有返回值的提交任务方法
public Future<?> submit(Runnable task) {
// 任务不能为空
if (task == null) throw new NullPointerException();
// 用FutureTask包装一下任务,方便调用set方法封装返回值
RunnableFuture<Void> ftask = newTaskFor(task, null);
// 提交任务
execute(ftask);
return ftask;
}
// ThreadPoolExecutor对execute方法的实现,没有返回值
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
// 获取ctl的值
int c = ctl.get();
// 如果工作线程数小于核心线程数
if (workerCountOf(c) < corePoolSize) {
// 添加核心工作线程去处理任务
if (addWorker(command, true))
// 添加成功,核心线程会去执行这个任务,结束
return;
// 添加核心工作线程失败,重新获取ctl的值
// 可能线程池状态发生了改变,也可能两个线程同时execute,但只有一个核心线程
c = ctl.get();
}
// 如果线程池状态是RUNNING,往工作队列中添加任务
if (isRunning(c) && workQueue.offer(command)) {
// 添加任务成功,再次获取ctl的值
int recheck = ctl.get();
// 再次判断是否是running状态,如果不是,从队列中删除这个任务
if (! isRunning(recheck) && remove(command))
// 不在接收新任务,执行拒绝策略
reject(command);
// 线程池状态正常 || 队列删除失败
// 如果工作线程数为0
else if (workerCountOf(recheck) == 0)
// 添加一个非核心线程去处理队列中的任务,因为队列中有任务
// 通过这个判断可能是用户将核心线程数设置为0,或者配置了allowCoreThreadTimeOut为true
addWorker(null, false);
}
// 线程池状态不正常 || 任务入队失败了(可能队列满了)
// 创建非核心线程去处理任务
else if (!addWorker(command, false))
// 如果失败,可能线程数满了或线程池状态改变了,执行拒绝策略
reject(command);
}
2.addWorker方法分析
添加工作线程,去执行任务
代码如下:
// 添加工作线程去处理任务
// core: 是否是核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
// 给外层死循环起个名字,这个循环全是校验逻辑
retry:
for (;;) {
int c = ctl.get();
// 高三位的值,获取线程池状态
int rs = runStateOf(c);
// SHUTDOWN:0
// 如果线程池状态大于等于0,则状态可能是除了RUNNING外其他任何状态
// 此时代表着不在接收任何外部任务
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
// 状态不正确,不需要创建工作线程去处理任务
return false;
// 再开启一个死循环
for (;;) {
// 获取工作线程数
int wc = workerCountOf(c);
// 工作线程数是否大于上限
if (wc >= CAPACITY ||
// 判断工作线程数是否超过了用户设定的上限
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// CAS增加工作线程数
if (compareAndIncrementWorkerCount(c))
// 校验结束
break retry;
// CAS失败,再次判断线程池状态是否发生了改变,如果改变,继续校验
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:设置state,保存任务,使用ThreadFactory创建线程
w = new Worker(firstTask);
// 从worker中获取线程
final Thread t = w.thread;
// 如果线程为null,可能是线程工厂创建线程时发生了问题
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());
// 如果小于0,则状态为Running状态
if (rs < SHUTDOWN ||
// 状态不是running,可能是shutdown,结合execute方法,任务可能为null
(rs == SHUTDOWN && firstTask == null)) {
// 此时线程还没有被start,如果已经开始执行了报错
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();
}
// 如果工作线程创建成功,调用Woker的runWorker方法,执行任务
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// 如果工作线程启动失败,从workers中删除当前的worker
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
retry死循环,全是校验是否可以创建工作线程去执行任务
3.内部类Worker分析
// 继承了AQS,实现了Runnable,重写了run方法,run方法中执行具体的任务
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
// 执行任务的线程,执行run()后会跳转到Worker重写的run方法中,然后执行firstTask.run()
final Thread thread;
// 任务
Runnable firstTask;
// 完成任务数
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
// 状态设置为-1,根据interruptIfStarted方法可以判断,这个状态无法设置中断标志位
setState(-1);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
// 重写了run方法,去执行具体的任务
// 由于创建woker时将this传进去,其实执行线程的start时执行的是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;
}
// 修改state的值
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(); }
// 执行shutDownDnow,中断线程时 ,会先执行该方法
// 设置中断标志位
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
4.分析runWorker方法
执行任务,如果是非核心线程,或者核心线程也可以过期回收掉,或者线程池即将关闭则getTask返回null,线程结束,等待被回收掉
反之,执行获取到的任务,如果核心线程不能被回收,getTask会阻塞住,一直到获取到任务
public void run() {
runWorker(this);
}
// 执行任务
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// 将state设置成0,意味着可以设置中断标志位
w.unlock();
boolean completedAbruptly = true;
try {
// 任务为null时,从阻塞队列中获取任务,获取不到一直等待
while (task != null || (task = getTask()) != null) {
w.lock();
// 如果线程池状态大于等于STOP && 没有设置中断位 => 设置中断位
// 如果线程被设置中断位(会恢复) && 线程池状态大于等于STOP && 没有设置中断位 => 设置中断位
if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
// 执行任务前先执行before钩子函数
beforeExecute(wt, task);
Throwable thrown = null;
try {
// 启动线程。如果使用submit提交方式,会跳转到FutureTask重写的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++;
// 将状态重新设置为0,此时shutDwon方法可以将当前线程设置中断位
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 判断是否清除当前工作线程
processWorkerExit(w, completedAbruptly);
}
}
// 如果使用了submit提交方式,会执行到FutureTask的run方法中
public void run() {
// 是否是NEW状态
if (state != NEW ||
// 是NEW状态,CAS将执行线程改成当前线程,如果失败,说明有其他执行这个任务
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
// 有其他线程执行这个任务
return;
try {
Callable<V> c = callable;
// 确保初始化了
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行任务
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
// 任务执行结束,调用set方法封装返回结果,将结果赋值给了outcome参数
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
5.getTask方法分析
获取任务
1,如果是临时线程并且达到了过期时间仍然没有获取到任务,返回null,线程被回收掉
2,如果核心线程可以被回收,并且过期时间内没有获取到任务,返回null,线程被回收
3,线程池状态即将关闭,返回null
4,如果核心线程不能被回收,执行take方法,一直到获取到任务才能返回
5,如果是临时线程,执行poll(keepAliveTime, unit),如果获取到任务,则继续执行任务,获取不到线程被回收掉
// 获取任务
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
// 死循环
for (;;) {
int c = ctl.get();
// 高三位,获取线程池状态
int rs = runStateOf(c);
// 线程池状态大于等于SHUTDOWN && (状态大于等于STOP || 没任务了)
// 线程池即将关闭 || 没任务了
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 工作线程数减一
decrementWorkerCount();
// 返回null时,runWorker的while循环会结束,线程运行结束,等待GC会被回收
return null;
}
// 工作线程数
int wc = workerCountOf(c);
// Are workers subject to culling?
// 可以认为成:当前是否是核心线程(线程是否是临时线程,可以被回收)
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// timedOut在第二次循环时才可能为true
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
// ---------------------------- 以上代码判断线程是否可以返回null,从而结束掉runWorker的死循环 ----------------------------
// 从阻塞队列中获取任务
// 如果当前是核心线程就执行take阻塞方法,非核心线程执行poll方法,是为了结束掉runWorker的while循环,回收线程
// 如果核心线程也可以过期结束掉,也执行poll方法
try {
Runnable r = timed ?
// 在时间内获取任务,获取不到返回null
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
// 阻塞方法,一直获取任务
workQueue.take();
// 获取到任务就返回
if (r != null)
return r;
// 获取不到,超时了
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
总结
待总结