分析下继承关系:1、ThreadPoolTaskExecutor extends (2)ExecutorConfigurationSupportimplements (3)AsyncListenableTaskExecutor, (4)SchedulingTaskExecutor2、 ExecutorConfigurationSupport extends CustomizableThreadFactory implements BeanNameAware, InitializingBean, DisposableBean3、public interface AsyncListenableTaskExecutor extends AsyncTaskExecutor4、public interface SchedulingTaskExecutor extends AsyncTaskExecutor从上继承关系可知:ThreadPoolExecutor是一个java类不提供spring生命周期和参数装配。ThreadPoolTaskExecutor实现了InitializingBean, DisposableBean ,xxaware等,具有spring特性AsyncListenableTaskExecutor提供了监听任务方法(相当于添加一个任务监听,提交任务完成都会回调该方法)简单理解:1、ThreadPoolTaskExecutor使用ThreadPoolExecutor并增强,扩展了更多特性2、ThreadPoolTaskExecutor只关注自己增强的部分,任务执行还是ThreadPoolExecutor处理。3、前者spring自己用着爽,后者离开spring我们用ThreadPoolExecutor爽。注意:ThreadPoolTaskExecutor 不会自动创建ThreadPoolExecutor需要手动调initialize才会创建如果@Bean 就不需手动,会自动InitializingBean的afterPropertiesSet来调initialize`
spring中的线程池加强和封装了原生threadpoolexecutor的特性,使之更加自然
@Configuration@EnableAsyncpublic class ExecutorConfig {private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);@Beanpublic Executor asyncServiceExecutor() {logger.info("start asyncServiceExecutor");ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//配置核心线程数executor.setCorePoolSize(5);//配置最大线程数executor.setMaxPoolSize(5);//配置队列大小executor.setQueueCapacity(99999);//配置线程池中的线程的名称前缀executor.setThreadNamePrefix("async-service-");// rejection-policy:当pool已经达到max size的时候,如何处理新任务// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//执行初始化executor.initialize();return executor;}}
- 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
- 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
- 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maxPoolSize,建新的线程来处理被添加的任务。
- 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maxPoolSize,那么通过handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
- 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
编写接口以及实现接口
函数式接口,用于添加一个函数(名字未知)
异步调用接口,用于接口的继承(推荐加上)
@FunctionalInterfacepublic interface ExecutorFunctional {void execute() throws Exception;}public interface AsyncService {void executeAsync(ExecutorFunctional target);}@Servicepublic class AsyncServiceImpl implements AsyncService {@Override@Async("asyncServiceExecutor")public void executeAsync(ExecutorFunctional target) {try {//这个execute中有一堆语句,我们可以将我们想要的任意类型的语句都写进去//因为这是执行接口的调用,内部的语句需要自行书写target.execute();} catch (Exception e) {e.printStackTrace();}}}//看起来就像是把一堆语句交给线程池执行了,不是吗?//我们只需要关注关注语句本身就行了@GetMapping("func")public void testFunctionalExecutor() {// CountDownLatch latch = new CountDownLatch(3);for (int i = 0; i < 3; i++) {asyncService.executeAsync(() -> {int random = (int) (2 + Math.random() * 8);log.info("线程睡{}秒", random);Thread.sleep(random * 1000);// latch.countDown();log.info("子线程睡{}秒,执行完毕", random);});}// latch.await();log.info("主线程执行完毕");}
使用线程池的时候,先用@Async(“线程配置命名”)来让spring来说明想要进入哪一个线程池
线程拒绝策略
当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
线程池的默认拒绝策略为AbortPolicy,即丢弃任务并抛出RejectedExecutionException异常。
(1)AbortPolicy
AbortPolicy
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
A handler for rejected tasks that throws a {@code RejectedExecutionException}.
这是线程池默认的拒绝策略,在任务不能再提交的时候,抛出异常,及时反馈程序运行状态。如果是比较关键的业务,推荐使用此拒绝策略,这样子在系统不能承载更大的并发量的时候,能够及时的通过异常发现。
(2)DiscardPolicy
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
A handler for rejected tasks that silently discards therejected task.
使用此策略,可能会使我们无法发现系统的异常状态。建议是一些无关紧要的业务采用此策略。例如,本人的博客网站统计阅读量就是采用的这种拒绝策略。
(3)DiscardOldestPolicy
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
A handler for rejected tasks that discards the oldest unhandled request and then retries {@code execute}, unless the executor is shut down, in which case the task is discarded.
此拒绝策略,是一种喜新厌旧的拒绝策略。是否要采用此种拒绝策略,还得根据实际业务是否允许丢弃老任务来认真衡量。
(4)CallerRunsPolicy
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
1. A handler for rejected tasks that runs the rejected task directly in the calling thread of the {@code execute} method, unless the executor has been shut down, in which case the task is discarded.2.
如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务,我们可以通过代码来验证这一点:
