ThreadPoolExecutor源码

一、为什么要使用线程池:

线程池维持一定数量的线程不断得从队列中获取任务执行,相比不断的创建线程来执行任务而言,大大地提高了系统执行效率,因为频繁的创建和销毁线程很耗时间,提高线程的可管理性。

二、线程池原理:

创建了一大堆线程和一个BlockingQueue队列,一大堆线程不断地从队列中获取任务并执行

三、线程池核心参数:

1)corePoolSize:核心线程池数量,当线程池中的线程数量大于corePoolSize,任务会放到队列中

2)maximumPoolSize:线程池最大能创建的线程数

3)workers:当前工作的works,其数量就是当前运行着的线程数量poolSize

4)keepAliveTime:线程存活时间,当 poolSize > corePoolSize 时,如果某线程空闲时间超过 keepAliveTime,该线程将终止,直至线程池中线程数量不大于corePoolSize

5)allowCoreThreadTimeOut:设置为true时,当线程池中的线程空闲时间超过keepAliveTime时,线程将终止

6)workQueue:任务队列,存放需要执行的任务

7)largestPoolSize:线程池中曾经创建的最大线程数

8)threadFactory:线程工厂,用于创建线程和命名线程名称

9)RejectedExecutionHandler handler:任务拒绝策略,默认是AbortPolicy,条件前提是任务数量超过maximumPoolSize时,直接拒绝任务并抛出RejectedExecutionException异常,拒绝策略共有以下四种(前提是任务数量超过maximumPoolSize):

AbortPolicy :拒绝任务并抛出RejectedExecutionException异常

DiscardPolicy:拒绝任务但不抛出异常

DiscardOldestPolicy:丢弃队列中最前面的任务,然后重新尝试执行任务(调用execute方法)

CallerRunsPolicy:调用线程执行该任务

注意:当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务

四、BlockingQueue队列类型简单了解一下:

(1) ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务, 必须初始化容量

(2) LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene,不初始容量的话,默认是Integer.MAX_VALUE;

(3) SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;

(4) PriorityBlockingQuene:具有优先级的无界阻塞队列;

五、源码如下:

1)结构如下:

ThreadPoolExecutor源码_第1张图片

2)主要参数:

ThreadPoolExecutor源码_第2张图片

ThreadPoolExecutor源码_第3张图片

ThreadPoolExecutor源码_第4张图片

ThreadPoolExecutor源码_第5张图片

3)构造方法:

ThreadPoolExecutor源码_第6张图片

ThreadPoolExecutor源码_第7张图片

ThreadPoolExecutor源码_第8张图片

ThreadPoolExecutor源码_第9张图片

4)执行过程

1.1 首先我们从execute方法分析,源码如下:

ThreadPoolExecutor源码_第10张图片

ThreadPoolExecutor源码_第11张图片

1.2 接下来看看addWorker方法,创建线程执行任务,源码如下:

ThreadPoolExecutor源码_第12张图片

ThreadPoolExecutor源码_第13张图片

1.3 分析一下内部类 Worker,核心部分就是run方法,源码如下:

ThreadPoolExecutor源码_第14张图片

1.4  Worker中run方法调用的核心方法就是 runWorker 方法,源码如下:

ThreadPoolExecutor源码_第15张图片

1.5 最后的重点就在于 getTask 方法,也就是获取任务,源码如下:

ThreadPoolExecutor源码_第16张图片

六、Executors 创建各种线程池

1、newFixedThreadPool 线程池,固定数量的线程数,队列是LinkedBlockingQueue,线程池的线程数量达corePoolSize后,即使线程池没有可执行任务时,也不会释放线程

FixedThreadPool的工作队列为无界队列LinkedBlockingQueue(队列容量为Integer.MAX_VALUE), 这会导致以下问题:

1)、线程池里的线程数量不超过corePoolSize,这导致了maximumPoolSize和keepAliveTime将会是个无用参数

2)、由于使用了无界队列, 所以FixedThreadPool永远不会拒绝, 即饱和策略失效

ThreadPoolExecutor源码_第17张图片

2、newSingleThreadExecutor 线程池,特殊的newFixedThreadPool 线程池,线程数量只有一个,队列是LinkedBlockingQueue,初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行.

由于使用了无界队列, 所以SingleThreadPool永远不会拒绝, 即饱和策略失效

ThreadPoolExecutor源码_第18张图片

ThreadPoolExecutor源码_第19张图片

3、newCachedThreadPool 线程池,线程数量是Integer.MAX_VALUE,这里可能导致一个线程无限增长的问题,最终资源耗尽,队列是SynchronousQueue

线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;

和newFixedThreadPool创建的线程池不同,newCachedThreadPool在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;

执行过程与前两种稍微不同:

(1) 主线程调用SynchronousQueue的offer()方法放入task, 倘若此时线程池中有空闲的线程尝试读取 SynchronousQueue的task, 即调用了SynchronousQueue的poll(), 那么主线程将该task交给空闲线程. 否则执行(2)

(2) 当线程池为空或者没有空闲的线程, 则创建新的线程执行任务.

(3) 执行完任务的线程倘若在60s内仍空闲, 则会被终止. 因此长时间空闲的CachedThreadPool不会持有任何线程资源.

ThreadPoolExecutor源码_第20张图片

你可能感兴趣的:(JDK)