ThreadPoolTaskExecutor和ThreadPoolExecutor区别
一、ThreadPoolExecutor
ThreadPoolExecutor是JDK中的线程池类,实现了Executor接口。 顾名思义,Executor 是一个专门用来处理多线程工作的接口,所有多线程处理相关的类都实现了这个接口。线程池主要提供一个线程队列,队列中保存着所有等待状态的线程,降低了线程频繁创建与销毁的开销,提高了响应的速度。ThreadPoolExecutor相关的继承实现类图如下所示:
1.1 线程池接口
ExecutorService为线程池接口,提供了线程池生命周期管理方法,继承自Executor接口。ThreadPoolExecutor为线程池实现类,提供了线程池的维护操作等相关方法,继承自AbstractExecutorService抽象类,AbstractExecutorService实现了ExecutorService接口。
1.2 线程池的体系结构
java.util.concurrent.Executor 线程使用和调度的根接口。
|--ExecutorService 子接口: 线程池的主要接口。
|--ThreadPoolExecutor 线程池的实现类
|--ScheduledExceutorService 子接口: 负责线程的调度。
|--ScheduledThreadPoolExecutor : 继承了ThreadPoolExecutor,实现了ScheduledExecutorService。
1.3 Executors工具类
Executors为线程池工具类,相当于一个工厂类,用来创建合适的线程池,返回ExecutorService接口类型的线程池。其包含如下方法:
ExecutorService newFixedThreadPool() : 创建固定大小的线程池。
ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
ExecutorService newSingleThreadExecutor() : 创建只有一个线程的线程池。
ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时执行任务。
其中,AbstractExecutorService是它们的抽象父类,继承自ExecutorService。ExecutorService 接口继承自Executor接口,增加了生命周期方法。
实际应用中,我一般比较喜欢使用Exectuors工厂类来创建线程池,其有五个方法,分别创建不同的线程池。例如,使用newFixedThreadPool创建一个制定大小的线程池,Exectuors工厂实际上就是调用传入默认参数的ThreadPoolExecutor构造方法。当然,我们也可以直接执行new ThreadPoolExecutor构造方法来创建线程池,传入需要的参数即可。
二、ThreadPoolTaskExecutor
ThreadPoolTaskExecutor这个类则是spring包下的,是sring为我们提供的线程池类。这里重点讲解这个类的用法。
2.1 创建ThreadPoolTaskExecutor线程池
可以使用基于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线程池的拒绝策略和处理流程。
2.2 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方法继续提交任务到工作队列,使得当前新到任务不丢失,阻塞等待空闲线程处理。