先来看调用的流程图.需要指明的是Worker类是ThreadPoolExecutor的内部类.abstractExecutorService是线程池的父类.thread参数被在Worker中声明
首先还是从ThreadPoolExecutor的构造函数说起.这个大家都比较熟悉了, 构造函数罗列如下,只罗列两个,一个是参数是最少的.另一个是全参数的:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
各个参数的说明把作者的原生说明贴出来
/**
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @throws IllegalArgumentException if one of the following holds:
* {@code corePoolSize < 0}
* {@code keepAliveTime < 0}
* {@code maximumPoolSize <= 0}
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
*/
这里需要指出的是,只所以有多个构造函数.是因为存在默认的参数.如果你没有需要就使用默认的就可以.默认的参数有2个分别是
默认的线程生成工厂
默认的拒绝处理.下面把构造函数的实现贴出来
当我们使用new ThreadPoolExecutor创建线程池后,就开始使用submit来提交Task了.我们就从这里开始跟踪,其在父类实现截图如下:
submit传进来的对象可以是callback也可以是Runnable.但都被封装成newTaksFor对象
然后调用execute开始执行.代码截图如下:
这段代码里面有一段说明文字,基本把作者的设计思想表达出来了.我写在我的理解吧
1.首先判断当前worker的总数量,如果小于核心工作数.那么直接增加worker来处理这个task.成功并返回.但是如果失败就到第二步
2.如果第一步失败.或者当前的worker不小于核心工作数.那么就会尝试把这个task加入到当前的workQueue.如果加成功了,就判断当前有没有worker.如果没有worker来处理,这个workqueue增加是没有意义的.在没有worker的情况下,先尝试把这个command删掉,然后reject结束.但是如果没有删成功,也没有worker.那么就需要增加worker来处理这个command了.
3.如果上两步条件都达不到,或者说都失败了.那就没办法,只能新开一个workder来解决问题,失败了就reject通告你就好了.
下面就来看下addWorker做了哪些事.
代码太长先分析前半部分.从for开始吧,上面那个if基本不可能成功,首先是获取当前worker的总数.如果总数大于CAPACITY或者大于最大worker数那会添加会失败.直接返回false.没有则继续,这个时候判断有没有其它线程在创建workercompareAndIncrementWorkerCount没有则break进入下一步.下半部截图如下:
这时候就开始new Worker了(要注意的是在Worker的构造函数里会创建thread对象).我们传进来的runnable对象作为参数生成Worker对象.然后下面就是一些判断,反正如果是判断成功则把worker增加到workers这个hashset里面.同时把workerAdded=true
接下来就是判断workerAdded有没有成功.如果有成功就t.tstart这个就是启动线程了,同时就workerstated=true.
最后如果workerStarted不为true则进行失败处理.到这里submit的操作就算执行完了.
这时我们关心的是线程start干了什么了,我们从默认的线程工厂创建的线程对象看
这个是默认的线程创建工厂.可以看到newThread时传进来了一个参数.而这个参数是worker对象本身代码截图如下
所以当线程start开始run的时候,调用的是worker对象的run看Worker run代码
runWorker就是调用我们task的run执行我们的代码了.理解完这段代码线程池的操作就结束了.老样子截图
这里首先是给task赋值我们传递进来的对象.执行我们自己的run.就是上图那个task.run
代码当我们自己的代码执行完了.task=null但并没有直接退出.
然后通过getTaks来处理workerqueue的task.这个时候我们设置的超时就启作用了,来看看getTask的代码吧
代码秀简明.首先获取当前的Worker的总数量,当总数大于核心worker数量时,会调用
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 取task.超时返回null.这个时候Worker就进入了退出处理操作了.
当不大于核心worker数量时会调用workQueue.take();这个阻塞操作,等待一直到有task产生为止.
从上面的代码可以看出,如果queue里面的数据量很大一直有task.那么就不会超时.相当更多的核心worker在一起干活.但是当queue数据量很小时,哪个worker会退出保证当前的worker数量不大于核心数量.这个看运气,并不是谁先加进来谁就是核心.
好了到这里结束.