一、为什么要使用线程池:
线程池维持一定数量的线程不断得从队列中获取任务执行,相比不断的创建线程来执行任务而言,大大地提高了系统执行效率,因为频繁的创建和销毁线程很耗时间,提高线程的可管理性。
二、线程池原理:
创建了一大堆线程和一个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)结构如下:
2)主要参数:
3)构造方法:
4)执行过程
1.1 首先我们从execute方法分析,源码如下:
1.2 接下来看看addWorker方法,创建线程执行任务,源码如下:
1.3 分析一下内部类 Worker,核心部分就是run方法,源码如下:
1.4 Worker中run方法调用的核心方法就是 runWorker 方法,源码如下:
1.5 最后的重点就在于 getTask 方法,也就是获取任务,源码如下:
六、Executors 创建各种线程池
1、newFixedThreadPool 线程池,固定数量的线程数,队列是LinkedBlockingQueue,线程池的线程数量达corePoolSize后,即使线程池没有可执行任务时,也不会释放线程
FixedThreadPool的工作队列为无界队列LinkedBlockingQueue(队列容量为Integer.MAX_VALUE), 这会导致以下问题:
1)、线程池里的线程数量不超过corePoolSize,这导致了maximumPoolSize和keepAliveTime将会是个无用参数
2)、由于使用了无界队列, 所以FixedThreadPool永远不会拒绝, 即饱和策略失效
2、newSingleThreadExecutor 线程池,特殊的newFixedThreadPool 线程池,线程数量只有一个,队列是LinkedBlockingQueue,初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行.
由于使用了无界队列, 所以SingleThreadPool永远不会拒绝, 即饱和策略失效
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不会持有任何线程资源.