接文章Java8线程池——底层为LinkedBlockingQueue的ThreadPoolExecutor,文章中简单介绍了线程池保持线程,并且从阻塞队列中获取任务执行的流程。本篇文章详细介绍线程池的几个重要的参数,以重要参数为线索更详细剖析线程池的实现细节。
参数corePoolSize——线程池的基本大小
线程池的目标大小,即在没有任务执行时线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。
- 没有任务执行时线程池的大小,具体说是一个延时初始化,在线程池初始化的时候,并不会同时初始化线程,只有当任务到来时才会进行线程的创建,并且线程创建后会把这个任务作为其第一个任务执行。
- 只有在工作队列满了,这里的队列就是线程池中定义的阻塞队列,阻塞队列可以设置大小,默认的长度是整型的最大值Integer.MAX_VALUE。
参数maximumPoolSize——线程池的最大线程数
表示可以同时活动的线程数量的上限。如果某个线程的空闲时间超过了存活时间,那么将被标记为可回收的,并且当线程池的当前大小超过了基本大小时,这个线程将被终止。其实在实际之中还有一个参数的设置allowCoreThreadTimeOut,设置true,允许线程池中所有的线程超时,这时不再需要线程数量超过基本大小这个判断条件。
代码片段如下:
- 如何实现的超时时间判断?利用阻塞队列的等待超时机制实现,如果在设定的时间内取不到任务,返回null,会把变量timedOut设置为true,表示已经超时。
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
- 在参数allowCoreThreadTimeOut和判断当前线程是否超过基本大小共同影响下,设置timed参数的值。
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
- 变量timedOut和timed同时为true时,根据判断会终止线程。否则线程继续for循环进行自旋,等待任务的到来。
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c)
return null;
continue;
}
getTask方法如下图:
参数keepAliveTime——超出基本大小的线程空闲状态的最长时间
如果变量allowCoreThreadTimeOut设置为true,所有线程都会可能超时,默认为false,只有超出基本大小的线程会超时。线程空闲的时间超过keepAliveTime,并且线程数量大于基本大小,线程会被终止。具体实现参看上面参数maximumPoolSize中讲解的代码。
RejectedExecutionHandler饱和策略
当有界队列被填满后,饱和策略发挥作用,可以通过调用setRejectedExecutionHandler设置。默认策略是抛出未检查的异常RejectedExecutionException,调用者可以捕获这个异常,根据需求处理,也可以快速定位到队列被填满。另外,还有策略直接抛弃到来的任务,或者抛弃下一个将被执行的任务,尝试提交新的任务。最后还可以实现将任务既不遗弃,也不抛出异常,而是把任务回退给调用者。在ThreadPoolExecutor中对应的类分别是:AbortPolicy,DiscardPolicy,DiscardOldestPolicy和CallerRunsPolicy。