Spring自带的线程池ThreadPoolTaskExecutor
文章系转载,方便整理和归纳,源文地址:https://zhuanlan.zhihu.com/p/346086161
前言
上一篇分享了JDK自带的线程池ThreadPoolExecutor的配置和参数详解,然而我们实际开发中更多的是使用SpringBoot来开发,Spring默认也是自带了一个线程池方便我们开发,它就是ThreadPoolTaskExecutor,接下来我们就来聊聊Spring的线程池吧。
Spring默认线程池simpleAsyncTaskExecutor
Spring异步线程池的接口类是TaskExecutor,本质还是java.util.concurrent.Executor,没有配置的情况下,默认使用的是simpleAsyncTaskExecutor。
@Async演示Spring默认的simpleAsyncTaskExecutor
@Component
@EnableAsync
public class ScheduleTask {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@Async@Scheduled(fixedRate = 2000)public void testScheduleTask() {try {Thread.sleep(6000);System.out.println("Spring1自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date()));} catch (InterruptedException e) {e.printStackTrace();}}@Async@Scheduled(cron = "*/2 * * * * ?")public void testAsyn() {try {Thread.sleep(1000);System.out.println("Spring2自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date()));} catch (Exception ex) {ex.printStackTrace();}}
}
从运行结果可以看出Spring默认的@Async用线程池名字为SimpleAsyncTaskExecutor,而且每次都会重新创建一个新的线程,所以可以看到TaskExecutor-后面带的数字会一直变大。
simpleAsyncTaskExecutor的特点是,每次执行任务时,它会重新启动一个新的线程,并允许开发者控制并发线程的最大数量(concurrencyLimit),从而起到一定的资源节流作用。默认是concurrencyLimit取值为-1,即不启用资源节流。
Spring的线程池ThreadPoolTaskExecutor
上面介绍了Spring默认的线程池simpleAsyncTaskExecutor,但是Spring更加推荐我们开发者使用ThreadPoolTaskExecutor类来创建线程池,其本质是对java.util.concurrent.ThreadPoolExecutor的包装。
这个类则是spring包下的,是Spring为我们开发者提供的线程池类,这里重点讲解这个类的用法。
Spring提供了xml给我们配置ThreadPoolTaskExecutor线程池,但是现在普遍都在用SpringBoot开发项目,所以直接上yaml或者properties配置即可,或者也可以使用@Configuration配置也行,下面演示配置和使用。
ThreadPoolTaskExecutor配置
application.properties
# 核心线程池数
spring.task.execution.pool.core-size=5
# 最大线程池数
spring.task.execution.pool.max-size=10
# 任务队列的容量
spring.task.execution.pool.queue-capacity=5
# 非核心线程的存活时间
spring.task.execution.pool.keep-alive=60
# 线程池的前缀名称
spring.task.execution.thread-name-prefix=god-jiang-task-
AsyncScheduledTaskConfig.java
@Configuration
public class AsyncScheduledTaskConfig {@Value("${spring.task.execution.pool.core-size}")private int corePoolSize;@Value("${spring.task.execution.pool.max-size}")private int maxPoolSize;@Value("${spring.task.execution.pool.queue-capacity}")private int queueCapacity;@Value("${spring.task.execution.thread-name-prefix}")private String namePrefix;@Value("${spring.task.execution.pool.keep-alive}")private int keepAliveSeconds;@Beanpublic Executor myAsync() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//最大线程数executor.setMaxPoolSize(maxPoolSize);//核心线程数executor.setCorePoolSize(corePoolSize);//任务队列的大小executor.setQueueCapacity(queueCapacity);//线程前缀名executor.setThreadNamePrefix(namePrefix);//线程存活时间executor.setKeepAliveSeconds(keepAliveSeconds);/*** 拒绝处理策略* CallerRunsPolicy():交由调用方线程运行,比如 main 线程。* AbortPolicy():直接抛出异常。* DiscardPolicy():直接丢弃。* DiscardOldestPolicy():丢弃队列中最老的任务。*/executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//线程初始化executor.initialize();return executor;}
}
在方法上添加@Async注解,然后还需要在@SpringBootApplication启动类或者@Configuration注解类上 添加注解@EnableAsync启动多线程注解,@Async就会对标注的方法开启异步多线程调用,注意,这个方法的类一定要交给Spring容器来管理
@Component
@EnableAsync
public class ScheduleTask {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@Async("myAsync")@Scheduled(fixedRate = 2000)public void testScheduleTask() {try {Thread.sleep(6000);System.out.println("Spring1自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date()));} catch (InterruptedException e) {e.printStackTrace();}}@Async("myAsync")@Scheduled(cron = "*/2 * * * * ?")public void testAsyn() {try {Thread.sleep(1000);System.out.println("Spring2自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date()));} catch (Exception ex) {ex.printStackTrace();}}
}
以上从运行结果可以看出,自定义ThreadPoolTaskExecutor可以实现线程的复用,而且还能控制好线程数,写出更好的多线程并发程序。
另外需要注意的是:关于注解失效需要注意以下几点
- 注解的方法必须是public方法
- 方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的,因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。
- 异步方法使用注解@Async的返回值只能为void或者Future
上面注解中已经注释了参数的详解,这里重点讲解一下Spring线程池的拒绝策略和处理流程。
拒绝策略
rejectedExectutionHandler参数字段用于配置绝策略,常用拒绝策略如下
- AbortPolicy:用于被拒绝任务的处理程序,它将抛出RejectedExecutionException
- CallerRunsPolicy:用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。
- DiscardOldestPolicy:用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。
- DiscardPolicy:用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。
处理流程
- 查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第二步。
- 查看任务队列是否已满,不满就将任务存储在任务队列中,否则执行第三步。
- 查看线程池是否已满,即就是是否达到最大线程池数,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。
总结
这次分享了Spring自带的线程池ThreadPoolTaskExecutor的配置和使用,并且讲了线程池的参数和处理流程。当然Spring提供了7个线程池的实现,感兴趣的可以自行了解~~~
希望以上的分享对你们有所帮助,喜欢的点个赞支持一下我吧~~~
Spring自带的线程池ThreadPoolTaskExecutor相关推荐
- springboot自带的线程池ThreadPoolTaskExecutor、ThreadPoolTaskScheduler的深入应用——异步任务监听回调,任务中断案例
一.常用的的线程池对象 1.jdk原生的两个常用线程池对象 ThreadPoolExecutor.ScheduledThreadPoolExecutor,后者继承前者,主要增加了任务调度相关的一些方法 ...
- SpringBoot线程池ThreadPoolTaskExecutor和@Async异步方法浅解及代码应用示例
目录 线程池执行顺序 线程池配置策略 Spring线程池的配置类: Spring有.无返回值的异步调用示例 自定义的异步方法类代码: 测试类代码 常见问题: 参考文章: 线程池执行顺序 核心线程数(C ...
- Spring 已经实现的线程池
在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的.但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务.其实在Spring 3.x之后,就 ...
- 在spring boot应用监控线程池的状态
今天在公众号中看到了一篇教你如何监控 Java 线程池运行状态 ,才记起来我之前也做过一样的事情,但是没有记录下来,今天有空记下来.我会首先介绍一下监控的背景,监控方式,实例代码及效果3个部分介绍. ...
- Java自带的线程池Executors.newFixedThreadPool
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程 ...
- Java自带的线程池ThreadPoolExecutor详细介绍说明和实例运用
Java 5 开始,Java 提供了自己的线程池.线程池就是一个线程的容器,每次只执行额定数量的线程. java.util.concurrent.ThreadPoolExecutor 就是这样的线程池 ...
- (原创)使用AsyncTask(带修改线程池方式)+自定义ImageLoader+LRU算法对图片三级缓存及其显示优化(只有在ListView滑动停止的时候才去网络请求获取图片数据)
前言: 以前看了些关于图片优化处理缓存比较全的视频(感谢慕风网),现在回顾觉得还是挺好的也就总结出来下,感觉针对图片做处理这块还真的用的比较多,本文章只要使用异步线程AsyncTask.自定义的Ima ...
- 深入理解Java自带的线程池和缓冲队列
https://www.cnblogs.com/xiguadadage/p/10243332.html
- java executor spring_java 线程池(ExecutorService与Spring配置threadPoolTaskExecutor)
一.java ExecutorService实现 创建ExecutorService变量 private ExecutorService executor = null 2.执行对应任务时,首先生成线 ...
- 自定义spring kafka consumer 线程池
序 本文讲述一下如何自定义spring kafka的consumer线程池 KafkaMessageListenerContainer spring-kafka-1.2.3.RELEASE-sourc ...
最新文章
- JAVA中的反射()
- Scrum敏捷开发沉思录
- python pip install pipenv失败_pipenv 无法创建依赖情况应该怎么处理?大家有什么好的建议吗?...
- C++string中find_first_not_of()函数和find_last_not_of()函数
- linux下更换pip源
- thinkphp 编辑器kindeditor
- C++容器遍历时删除元素
- 绝地求生7月5日服务器维护,绝地求生7月5日更新到几点 吃鸡更新维护公告
- P1420 最长连号(python3实现)
- Qt工作笔记-ui文件连接信号与槽
- 图像语义分割 -- UNET++
- 【CuteJavaScript】Angular6入门项目(1.构建项目和创建路由)
- docker常用功能操作记录5
- SAC案例精选:SAC Planning实现SOP(销售与运营计划)模拟沙盘
- 微信小程序账号注册流程
- Deep Learning Chapter02:Python基础语法回顾
- ThinkPad E450 进入dos界面快捷键 按F1
- 硕士管理类199联考论说文真题素材
- 一年200多天不上网 看远望7号船员怎么玩抖音
- (补充)微信长按识别二维码 -- 页面多个二维码如何识别?(二)