线程池源码阅读笔记
本文主要是讲在下阅读java 线程池源码的历程,在这里既是分享,也是笔记!废话少说,进入正题!
多线程编程是每一个java开发人员都绕不开的话题,而说起多线程编程,又绕不开线程池!在最近的工作之中,因为业务量级在日/亿级别的原因,并发编程成为了我做开发和设计工作的日常。日常以久,就深刻的理解了线程是多么宝贵的资源!
为何要将线程池化,原因无非有三:
1 new Thread() -->线程工作--->销毁thread;这在面向对象的语言中是一种资源浪费,每个object的示例的创建和销毁都需要机器执行对应的指令!
2 如果有一个线程,已经提前创建好,在任务代码提交过来时,直接执行任务,比起new Thread.run(提交的任务) 是要快的
3 机器资源是有限的,如果线程无限创建,资源终有尽时,而线程池可设置最大线程数量,防止资源耗尽!
打开IDEA, 找到rt.jar下 java.util.concurrent 包下的 ThreadPoolExecutor 查看其实现,由上至下阅读其核心代码如下!
在接口 Executor 上看到大神 Doug Lea 定义线程池于1.5版本!其核心方法 void execute(Runnable command); 可直白理解为执行一个线程命令
Executor 有诸多实现,因为要分析 ThreadPoolExecutor 的源码,这里不分析其他!
ExecutorService 继续了 Executor 接口,扩展了如下方法(凭借英语二级的水平,翻译代码注释核心如下):
void shutdown(); // 线程池停止,不接受新任务,执行完已提交的任务
List<Runnable> shutdownNow() //线程池停止,尝试停止所有正在执行的任务,返回等待队列中的任务
boolean isShutdown(); //是否已停止
<T> Future<T> submit(Callable<T> task); // 提交任务,返回一个future 任务执行完毕后future可以get返回结果
<T> Future<T> submit(Runnable task, T result); // // 提交任务和结果,返回一个future ,任务执行完毕返回此结果
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) //提交一批任务,任务都执行完毕后返回结果
<T> T invokeAny(Collection<? extends Callable<T>> tasks) //提交一批次任务,任一任务完毕后返回结果
以上为 ExecutorService 继承 Executor 接口后扩展的接口, 抽象类 AbstractExecutorService 提供了基本的简单实现,这里只分析 doInvokeAny方法(invokeAll 方法可参考此实现)!代码如下:
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,boolean timed, long nanos)throws InterruptedException, ExecutionException, TimeoutException {
if (tasks == null) throw new NullPointerException();
int ntasks = tasks.size();
if (ntasks == 0) throw new IllegalArgumentException(); //以上为参数校验,不必多说
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);// 创建任务集合等长的fature集合
ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(this); //具体任务执行,将this传入用于获取excutor执行,同时内部维护着一个任务完成的结果completionQueue
try {
ExecutionException ee = null;
final long deadline = timed ? System.nanoTime() + nanos : 0L; // 超时判断,比如30s超时,会把系统当前时间纳秒时间戳+30*1000000000 纳秒得到的时间戳当做截止时间
Iterator<? extends Callable<T>> it = tasks.iterator();
futures.add(ecs.submit(it.next()));//submit任务后实际用的是this.excute执行任务,但是会把任务 it.next()包装成一个ExecutorCompletionService内部类 QueueingFuture(基础FutureTask)对象,
// 在 QueueingFuture执行完毕调用done()方法将结果写入ExecutorCompletionService.completionQueue中
--ntasks; //已经submit一次,所以-1
int active = 1;
for (;;) {
Future<T> f = ecs.poll();
if (f == null) {//如果第一个提交的任务为执行完毕,即queueingFacture尚未调用done方法add结果
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next())); //再循环内依次提交执行任务
++active;
}
else if (active == 0)
break;
else if (timed) {//如果需要判断超时,尝试poll结果,用传入的nanos(比如2000000)参数作为超时时间,单位纳秒,如果poll为空,抛出超时,如果尚未超时,但是耗时1000000,那么剩余的nanos是1000000,下次再poll的超时时间就是1000000
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
}
else
f = ecs.take();
}
if (f != null) {
--active; //这里就是invokeAny的关键意思,如果poll到的结果,那代表已经有一个任务执行完毕,add了结果future进入队列,则返回此结果
try {
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
}
if (ee == null) {ee = new ExecutionException(); throw ee;}
} finally {//有结果返回,以满足了任一任务执行成功的条件,剩余的不必继续执行
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
} //以上为doInvokeAny()方法阅读理解
下面直接阅读ThreadPoolExecutor源码,阅读其源码必须要理解其各个熟悉的含义,理解如下:
核心参数:count_bits=29 二进制左移位数,用于状态计算,一下的状态int参数皆是左移29位
CAPACITY,
RUNNING (-1), 正常状态,接受任务,运行任务,创建后默认设置为此状态
SHUTDOWN(0), 不接受新任务,但是会处理已经添加到queue中的任务
STOP(1), 不接受新任务,不再处理queue中任务,并尝试停止正在执行的任务
TIDYING(2), 所有任务执行完毕后的状态,是指ctlof()返回0 ,不再有任务未执行
TERMINATED(3), 线程池彻底停止的状态
注:后续的线程池的许多操作都要反复判断线程池状态,主要就是毕竟这几个状态的大小值,需要牢记其大小顺序
BlockingQueue<Runnable> workQueue;{任务集合}
ReentrantLock mainLock = new ReentrantLock();// 锁,用于操作工作线程集合时使用
HashSet<Worker> workers = new HashSet<Worker>(); 工作线程集合
Condition termination = mainLock.newCondition(); 锁的等待条件
largestPoolSize 最大容量 completedTaskCount 已完成任务数
ThreadFactory threadFactory; 创建工作线程的工厂
RejectedExecutionHandler handler 任务拒绝策略
keepAliveTime 一个工作 线程等待执行任务的最大等待时间
allowCoreThreadTimeOut 是否允许工作线程闲置超时,默认未false,即核心的core工作线程可以无限闲置存活
corePoolSize 在没有设置allowCoreThreadTimeOut的情况下存活的闲置等待线程数,也就是核心线程数
maximumPoolSize 最大线程数
RejectedExecutionHandler defaultHandler 默认的拒绝策略 为 AbortPolicy(),抛出异常
核心内部类 即工作线程 Worker实现原理:
首先 Worker 继承了 AbstractQueuedSynchronizer 所以天生支持同步,同时又实现了Runnable
Worker属性:thread 实际工作的线程, Runnable firstTask 初始化时第一个执行的任务, long completedTasks 已经执行完的任务数
当worker线程启动后,run方法内部调用的是 runWorker(); runworker源码核心如下:
runWorker(Worker w){
获取 worker的firsttask;调用worker.unlock
获取执行任务,如果firttask不为空,则为此任务,如果为空,从taskQueue中获取;
使用w.lock();锁定worker;
判断线程池状态,如果状态>=stop状态,则停止当前线程,不再执行任务;
//执行任务前置处理 -->执行任务---->执行任务后置处理---->task指向null,回收旧任务,worker完成任务数+1,woker.unlock;
以上是获取到任务的状态,如果不能再继续获取到可执行任务,最后会调用processWorker(worker,false);其原理如下:
{
获取到threadpoolexecutor.mainlock(),然后才可以操作worker线程集合
把线程池已完成的任务总数加上此worker已完成的任务数,将此worker从workers中remove,是否mainlock;
调用tryTerminate(),其核心是:如果线程池是running状态,或者已经terminated,或者是shutdown状态但是队列不为空,都直接返回,如果不是,且worker线程存在,调用interruptIdleWorkers(),便利workers集合,获取每个worker.thread,依次 停止
在tryTerminate()执行完后,如果线程池还是running状态或shutdown状态,如果需要维护corepoolsize线程,尝试 添加空worker线程
}
}
以上是核心方法runWorker()的逻辑,也就是线程池运行时工作的流程,其中有getTask()方法被调用,其核心流程如下:
{
先判断线程池状态,如果大于等于shutdown,或者大于等于了stop状态且队列为空,cas该状态,然后返回
接着继续判断,这里先根据线程池的属性allowCoreThreadTimeOut,再根据当前worker线程数是否大于corepoolsize,判断是否要判断超时
如果工作线程数已超出最大线程数,或者需要判断超时且已超时,当这2个条件满足其一后,又满足了线程池状态已大于stop或任务队列已空,cas线程池状态修改-1;返回;
如果以上代码均未实现,代表线程池任处于正常工作中,那就从任务队列中poll一个任务返回,如果需要超时判断,使用线程池的keepAliveTime作为超时时间范围,纳秒做单位;
}
runWorker() 和getTask()构成了线程池工作的核心,而excute() 和 shutDown() 以及shutdownnow() 则是开发人员最常用的方法,以下对其做简单分析;
execute(Runnable command){
首先判断当前工作线程数是否小于corepoolsize; 如果小于,把传入的command当做参数调用addworker(command,true)方法,其简要逻辑如下:
{
如果线程池已不是running状态,或者在shutdown状态时队列不为空的条件已不能满足,则直接返回,不创建worker;
如果条件满足了addworker,将传入的command当做firsttask,创建worker对象,获取到线程池mainlock.lock,再次判断线程池状态,如果是running状态,或者是shutdow状态但是firsttask是空,添加创建worker至workers中,最后调用worker.start(),工作线程开始工作
}
如果当期工作线程数不小于corepoolsize,判断线程池是否是running,如果是,想任务队列workqueue添加传入的任务command,如果添加成功,再次判断;
判断线程池是否是running,如果不是,再移除任务command,使用拒绝策略拒绝任务;
如果以上条件都不成立,直接添加worker,如果失败,调用拒绝策略拒绝!至此,向线程池提交一个任务的execute()方法执行完毕!
}
shutdown() {
获取到线程池的mainlock.lock();
调用checkShutdownAccess()方法,检查是否可停止,主要是使用SecurityManager 逐次检验工作线程的thread的权限
将线程池状态设置为shutdown
便利工作线程集合workers,逐次停止worker,worker.thread.interrupt();
释放锁
}
shutdownNow(){
获取线程池的mainlock.lock();
检查是否可停止;
cas尝试将状态设置为stop;
停止所有worker工作;
最后,将任务队列中的所有任务add到一个arraylist中,作为方法返回值;
是否锁;
最后会再次调用一次tryTerminate(),其内部再次判断线程池状态,并且尝试停止所有worker、
}
线程池的其他方法多为辅助工作的方法,源码比较简单,不在多写!
线程池在队列已满,不能再接受新任务时,会调用拒绝策略拒绝新任务,ThreadPoolExecutor内部有四个实现了 RejectedExecutionHandler接口的内部类,(拒绝策略即调用该接口的 rejectedExecution()方法拒绝);
CallerRunPolicy:{
if(!e.isshutdown){
r.run(); 如果线程池不是停止状态,直接使用当前调用线程run目标任务的方法拒绝任务
}
}
AbortPolicy{
直接抛出RejectedExcutionException异常拒绝任务
}
DiscardPolicy{
不做任务处理,方法为空
}
DiscardOldestPolicy{
判断线程池是否已经shutdown,如果没有,poll出一个任务,然后调用excute,添加新任务
}
至此,ThreadPoolExecutor的源码阅读结束!
Executors提供了默认的5中线程池
Executors.newCachedThreadPool(); 原理是coresize为0,但是默认有存活时间,默认60s,队列未Integer.MAX, 即单一工作线程60s后无任务处理可清楚
Executors.newFixedThreadPool(); 原理即自定义参数,保留核心工作线程
Executors.newSingleThreadExecutor(); 原理是core线程数和max线程数都为1
Executors.newSingleThreadScheduledExecutor(); 使用使用ScheduledThreadPoolExecutor线程池类实现,coresize为1
Executors.newScheduledThreadPool; 使用ScheduledThreadPoolExecutor线程池类实现(todo 阅读其源码)
Executors.newWorkStealingPool() 使用ForkJoinPool 实现,慎用!