线程池的产生
- 创建、销毁线程伴随着系统开销、线程是可以重复适用的
- 线程并发数量过多,抢占系统资源从而导致阻塞
- 对线程要进行管理
Executor
具体实现类是ThreadPoolExecutor类
核心参数
corePoolSize
线程池的基本大小,即 在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超过这个数量的线程。在刚刚创建threadpoolexecutor的时候,线程不会立即启动,而是在有任务提交的时候才会启动,除非调用了prtestartCoreThread/prestartAllcoreThreads事先启动核心线程,没有任务执行的时候,线程池的大小不一定是corepoolSize
maximumPoolSize
线程池允许的最大线程数,线程池中的当前线程数目不会超过这个值,如果队列中任务已满,并且当前线程个数小于maximumpoolsize,那么会创建新的线程来执行任务。
keepAliveTime
如果一个线程处于空闲状态的时间超过该属性,就会因为超时而退出,如果这个线程是核心线程,线程退出取决于allowCoreThreadTimeOut
queueCapacity
阻塞队列 任务队列容量
blockingQueue
1、ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
2、LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列,使用此阻塞队列时maximumPoolSizes就相当于无效
3、SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。可以避免在处理可能具有内部依赖性的请求集时出现锁。
4、PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
rejectedExecutionHandler
当线程池发生两种情况,此时拒绝增加新的任务,将会采用线程池的饱和策略
- 两种情况
- 当线程已经达到maxpoolsize时,且队列已满,会拒绝新任务
- 当线程池被调用shutdown时,会等待线程池里的任务执行完毕再shutdown
- 饱和策略的类型
- AbortPolicy 丢弃任务,抛运行时异常RejectedExecutionException 默认策略
- CallerRunsPolicy 由调用者线程执行任务,如果调用者已关闭,则丢弃
- DiscardPolicy 忽略
- DiscardOldestPolicy 从队列中踢出最先进入队列的惹怒五
- 实现RejectedExecutionHandler接口,可自定义处理器
执行顺序
1 | 1. 当线程数小于核心线程数时,创建线程。 |
线程池的关闭
shutdown 与shutdownnow
常见的几种线程池
Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池
线程池监控
actuator 组件来做线程池的监控。
ThreadPool api
参数设置的方法
任务的类型
- cpu密集型,
- 较小的线程池,cpu核心数+1,cpu密集型任务会使cpu使用率很高,若开过多的线程数,只会增加上下文切换的次数,带来额外的开销
- io密集型
- cpu使用率不高,可以让cpu在等待io的时候去处理别的任务,充分利用cpu的时间
- 混合型
- 分别用不同的线程池去处理,两个任务的结束时间取决与后执行完的任务,适用于两个任务的执行时间相差不大
2N+1
N为cpu核数
根据几个值设置
1 | * 需要根据几个值来决定 |
源码分析
线程池的创建
添加任务
任务的获取
任务的执行