这篇文章主要是对线程池做一个大致的介绍,下一篇文章会通过源码对线程池进行深入分析。
关于线程池,相信用过多线程的朋友们都是有所了解的,使用上比较简单,就算不知道内部实现也可以直接拿出来用。
1.引入线程池原因
如同数据库的连接池,http的连接池以及多线程的线程池,引入这些各种各样的池无非是为了两个原因:
1.创建连接或者线程需要消耗大量资源,使用池来管理可以避免频繁的创建与销毁开销;
2.控制连接或者线程的数量,例如使用线程池,线程的数量是可控的,避免无限制创建线程,耗尽服务器资源。
2.线程池分类
可缓存线程池 newCachedThreadPool 不对线程数做限制,线程空闲60s后会回收,任务新增时新建线程。
固定大小线程池 newFixedThreadPool 每次任务新增时新建线程直到达到设定线程最大值。
调度线程池 newScheduledThreadPool 调度线程池 创建一个不限制大小的线程池,可以执行调度任务。(我们公司早期没有调度框架时用来执行过定时任务)
单线程线程池 newSingleThreadPool 单工作线程按顺序执行的线程池
上述四种线程池本质上都是新建了一个ThreadPoolExecutor 对象,很明显它就是线程池的核心类,另外spring中线程池org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor类本质上也是依赖于ThreadPoolExecutor。一般除非场景简单,否则不推荐直接使用这几种,我们可以根据业务自定义一个线程池来执行业务。如何自定义呢?下面我们来看一下ThreadPoolExecutor它的构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize 核心线程数
maximumPoolSize 最大线程数
keepAliveTime 线程没有任务执行时最大存活时间
unit 时间单位
workQueue 当线程数目超过核心线程数时用于保存任务的队列
rejectHandler 饱和(拒绝)策略,任务大于最大线程数+阻塞队列数时的策略,默认是丢失并抛异常
threadFactory 执行程序创建新线程时使用的工厂
线程数小于核心线程数时,任务提交时,直接新增线程(就算有线程空闲);大于核心线程数时,将任务放入阻塞队列中,阻塞队列满后若核心线程数小于最大线程数,则继续新建线程去执行。
阻塞队列BlockingQueue
LinkedBlockingQueue
无界队列,默认无长度限制(Integer.Max),自定义线程池中queueCapacity参数为无界队列长度。注意当Executors.newFixedThreadPool定义线程池时,使用了无界队列且未限制长度,线程来不及处理导致队列大规模阻塞情况下可能导致内存cpu飙升。早期线上出现过类似问题,注意使用!!!
ArrayBlockingQueue
有界队列,遵循FIFO原则。
PriorityBlockingQueue
有界队列,可以自定义优先级,没用过。。。
SynchronousQueue
同步移交队列,不希望任务在队列中等待而是希望将任务直接移交给工作线程时使用。会直接将任务提交给线程执行,一般在线程数无限制时使用。spring自定义线程池不设定阻塞队列长度时会使用同步移交队列
rejectHandler中阻塞策略
AbortPolicy 默认的策略,中止并抛异常
DiscardPolicy 抛弃策略
DiscardOldestPolicy 抛弃旧任务策略,使用前最好计算一下并发线程数,项目中有出现过多线程跑数据丢了几千条。。。后面发现是被丢弃了
CallerRunsPolicy 调用者运行策略,让调用者直接运行,相当于主线程直接跑,这样就会阻塞到他跑完为止,其他线程没办法提交。
下面是我在项目中常用的spring线程池配置
<bean id="syncHistoryDataExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程数 -->
<property name="corePoolSize" value="${task.core_pool_size}" />
<!-- 最大线程数 -->
<property name="maxPoolSize" value="${task.max_pool_size}" />
<!-- 队列最大长度 -->
<property name="queueCapacity" value="${task.queue_capacity}" />
<!-- 线程池维护线程所允许的空闲时间,默认为60s -->
<property name="keepAliveSeconds" value="${task.keep_alive_seconds}" />
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$DiscardOldestPolicy" />
</property>
</bean>