文章系转载,方便整理和归纳,源文地址: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可以实现线程的复用,而且还能控制好线程数,写出更好的多线程并发程序。

另外需要注意的是:关于注解失效需要注意以下几点

  1. 注解的方法必须是public方法
  2. 方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的,因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。
  3. 异步方法使用注解@Async的返回值只能为void或者Future

上面注解中已经注释了参数的详解,这里重点讲解一下Spring线程池的拒绝策略和处理流程。

拒绝策略

rejectedExectutionHandler参数字段用于配置绝策略,常用拒绝策略如下

  • AbortPolicy:用于被拒绝任务的处理程序,它将抛出RejectedExecutionException
  • CallerRunsPolicy:用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。
  • DiscardOldestPolicy:用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。
  • DiscardPolicy:用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。

处理流程

  1. 查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第二步。
  2. 查看任务队列是否已满,不满就将任务存储在任务队列中,否则执行第三步。
  3. 查看线程池是否已满,即就是是否达到最大线程池数,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。

总结

这次分享了Spring自带的线程池ThreadPoolTaskExecutor的配置和使用,并且讲了线程池的参数和处理流程。当然Spring提供了7个线程池的实现,感兴趣的可以自行了解~~~

希望以上的分享对你们有所帮助,喜欢的点个赞支持一下我吧~~~

Spring自带的线程池ThreadPoolTaskExecutor相关推荐

  1. springboot自带的线程池ThreadPoolTaskExecutor、ThreadPoolTaskScheduler的深入应用——异步任务监听回调,任务中断案例

    一.常用的的线程池对象 1.jdk原生的两个常用线程池对象 ThreadPoolExecutor.ScheduledThreadPoolExecutor,后者继承前者,主要增加了任务调度相关的一些方法 ...

  2. SpringBoot线程池ThreadPoolTaskExecutor和@Async异步方法浅解及代码应用示例

    目录 线程池执行顺序 线程池配置策略 Spring线程池的配置类: Spring有.无返回值的异步调用示例 自定义的异步方法类代码: 测试类代码 常见问题: 参考文章: 线程池执行顺序 核心线程数(C ...

  3. Spring 已经实现的线程池

    在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的.但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务.其实在Spring 3.x之后,就 ...

  4. 在spring boot应用监控线程池的状态

    今天在公众号中看到了一篇教你如何监控 Java 线程池运行状态 ,才记起来我之前也做过一样的事情,但是没有记录下来,今天有空记下来.我会首先介绍一下监控的背景,监控方式,实例代码及效果3个部分介绍. ...

  5. Java自带的线程池Executors.newFixedThreadPool

    线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程 ...

  6. Java自带的线程池ThreadPoolExecutor详细介绍说明和实例运用

    Java 5 开始,Java 提供了自己的线程池.线程池就是一个线程的容器,每次只执行额定数量的线程. java.util.concurrent.ThreadPoolExecutor 就是这样的线程池 ...

  7. (原创)使用AsyncTask(带修改线程池方式)+自定义ImageLoader+LRU算法对图片三级缓存及其显示优化(只有在ListView滑动停止的时候才去网络请求获取图片数据)

    前言: 以前看了些关于图片优化处理缓存比较全的视频(感谢慕风网),现在回顾觉得还是挺好的也就总结出来下,感觉针对图片做处理这块还真的用的比较多,本文章只要使用异步线程AsyncTask.自定义的Ima ...

  8. 深入理解Java自带的线程池和缓冲队列

    https://www.cnblogs.com/xiguadadage/p/10243332.html

  9. java executor spring_java 线程池(ExecutorService与Spring配置threadPoolTaskExecutor)

    一.java ExecutorService实现 创建ExecutorService变量 private ExecutorService executor = null 2.执行对应任务时,首先生成线 ...

  10. 自定义spring kafka consumer 线程池

    序 本文讲述一下如何自定义spring kafka的consumer线程池 KafkaMessageListenerContainer spring-kafka-1.2.3.RELEASE-sourc ...

最新文章

  1. JAVA中的反射()
  2. Scrum敏捷开发沉思录
  3. python pip install pipenv失败_pipenv 无法创建依赖情况应该怎么处理?大家有什么好的建议吗?...
  4. C++string中find_first_not_of()函数和find_last_not_of()函数
  5. linux下更换pip源
  6. thinkphp 编辑器kindeditor
  7. C++容器遍历时删除元素
  8. 绝地求生7月5日服务器维护,绝地求生7月5日更新到几点 吃鸡更新维护公告
  9. P1420 最长连号(python3实现)
  10. Qt工作笔记-ui文件连接信号与槽
  11. 图像语义分割 -- UNET++
  12. 【CuteJavaScript】Angular6入门项目(1.构建项目和创建路由)
  13. docker常用功能操作记录5
  14. SAC案例精选:SAC Planning实现SOP(销售与运营计划)模拟沙盘
  15. 微信小程序账号注册流程
  16. Deep Learning Chapter02:Python基础语法回顾
  17. ThinkPad E450 进入dos界面快捷键 按F1
  18. 硕士管理类199联考论说文真题素材
  19. 一年200多天不上网 看远望7号船员怎么玩抖音
  20. (补充)微信长按识别二维码 -- 页面多个二维码如何识别?(二)

热门文章

  1. Mac 10.12安装迅雷2.7.2
  2. JS总结 循环 退出循环 函数
  3. 第一周 7.10-7.16
  4. 利用drozer进行Android渗透测试
  5. 四则运算2之设计思路篇
  6. DbEntry.Net---又一个国产开源ORM数据访问及 WEB 框架
  7. RIP路由六大防环机制
  8. java hibernate状态_Hibernate对象的三种状态
  9. 计算机网络性能(2)
  10. Security+ 学习笔记55 隐私和合规性