赞
踩
工作中发现, 在创建线程池的时候,有些同事使用ThreadPoolTaskExecutor,有些同事则使用ThreadPoolExecutor,容易令人混淆。于是,决定探究下这两个线程池的区别以及使用,也方便其他同学理解清楚。
ThreadPoolExecutor是JDK中的线程池类,实现了Executor接口。 顾名思义,Executor 是一个专门用来处理多线程工作的接口,所有多线程处理相关的类都实现了这个接口。线程池主要提供一个线程队列,队列中保存着所有等待状态的线程,降低了线程频繁创建与销毁的开销,提高了响应的速度。ThreadPoolExecutor相关的继承实现类图如下所示:
ExecutorService为线程池接口,提供了线程池生命周期管理方法,继承自Executor接口。ThreadPoolExecutor为线程池实现类,提供了线程池的维护操作等相关方法,继承自AbstractExecutorService抽象类,AbstractExecutorService实现了ExecutorService接口。
java.util.concurrent.Executor 线程使用和调度的根接口。
|--ExecutorService 子接口: 线程池的主要接口。
|--ThreadPoolExecutor 线程池的实现类
|--ScheduledExceutorService 子接口: 负责线程的调度。
|--ScheduledThreadPoolExecutor : 继承了ThreadPoolExecutor,实现了ScheduledExecutorService。
Executors为线程池工具类,相当于一个工厂类,用来创建合适的线程池,返回ExecutorService接口类型的线程池。其包含如下方法:
ExecutorService newFixedThreadPool() : 创建固定大小的线程池。
ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
ExecutorService newSingleThreadExecutor() : 创建只有一个线程的线程池。
ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时执行任务。
其中,AbstractExecutorService是它们的抽象父类,继承自ExecutorService。ExecutorService 接口继承自Executor接口,增加了生命周期方法。
实际应用中,我一般比较喜欢使用Exectuors工厂类来创建线程池,其有五个方法,分别创建不同的线程池。例如,使用newFixedThreadPool创建一个制定大小的线程池,Exectuors工厂实际上就是调用传入默认参数的ThreadPoolExecutor构造方法。当然,我们也可以直接执行new ThreadPoolExecutor构造方法来创建线程池,传入需要的参数即可。
ThreadPoolTaskExecutor这个类则是spring包下的,是sring为我们提供的线程池类。这里重点讲解这个类的用法。
可以使用基于xml配置的方式创建ThreadPoolTaskExecutor类型的线程池。
- <!-- spring线程池 -->
- <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
- <!-- 核心线程数 -->
- <property name="corePoolSize" value="10"/>
- <!-- 最大线程数 -->
- <property name="maxPoolSize" value="200"/>
- <!-- 队列最大长度 >=mainExecutor.maxSize -->
- <property name="queueCapacity" value="10"/>
- <!-- 线程池维护线程所允许的空闲时间 -->
- <property name="keepAliveSeconds" value="20"/>
- <!-- 线程池对拒绝任务(无线程可用)的处理策略 -->
- <property name="rejectedExecutionHandler">
- <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
- </property>
- </bean>
或者,通过配置类的方式配置线程池。
- @Configuration
- public class ExecturConfig {
- @Bean("taskExector")
- public Executor taskExector() {
-
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- int i = Runtime.getRuntime().availableProcessors();//获取到服务器的cpu内核
- executor.setCorePoolSize(5);//核心池大小
- executor.setMaxPoolSize(100);//最大线程数
- executor.setQueueCapacity(1000);//队列程度
- executor.setKeepAliveSeconds(1000);//线程空闲时间
- executor.setThreadNamePrefix("tsak-asyn");//线程前缀名称
- executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//配置拒绝策略
- return executor;
- }
然后,通过自动注入的方式注入线程池。
- @Resource(name="taskExecutor")
- ThreadPoolTaskExecutor taskExecutor;
- // 或者可以直接@Autowried
- @AutoWired
- ThreadPoolTaskExecutor taskExecutor
上面注释中已经解释了各参数的含义,这里重点讲解一下spring线程池的拒绝策略和处理流程。
rejectedExectutionHandler参数用于配置绝策略,常用拒绝策略如下:
AbortPolicy:用于被拒绝任务的处理程序,它将抛出RejectedExecutionException。
CallerRunsPolicy:用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。
DiscardOldestPolicy:用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。
DiscardPolicy:用于被拒绝任务的处理程序,默认情况下,它将丢弃被拒绝的任务。
线程池处理流程:
1.查看核心线程池是否已满,若不满,则创建一个线程执行任务;否则,执行第二步。
2.查看任务队列是否已满,若不满,则将任务存储在任务队列中;否则,执行第三步。
3.查看线程池是否已满,即是否达到最大线程池数,若不满,则创建一个线程执行任务;否则,就按照策略处理新到的任务。
如果任务队列已满,并且已达到最大处理线程数,这时候不想丢失当前新到任务,则可以使用自定义阻塞拒绝策略,如下所示:
- import java.util.concurrent.RejectedExecutionHandler;
- import java.util.concurrent.ThreadPoolExecutor;
-
- public class TaskRejectedHandler implements RejectedExecutionHandler{
- @Override
- public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
- try {
- executor.getQueue().put(r);
- } catch (Exception e) {
- }
- }
- }
如此,即可阻塞线程池execute方法继续提交任务到工作队列,使得当前新到任务不丢失,阻塞等待空闲线程处理。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。