0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Spring 的线程池应用

科技绿洲 来源:Java技术指北 作者:Java技术指北 2023-10-13 10:47 次阅读

我们在日常开发中,经常跟多线程打交道,Spring 为我们提供了一个线程池方便我们开发,它就是 ThreadPoolTaskExecutor ,接下来我们就来聊聊 Spring 的线程池吧。


使用@Async声明多线程

SpringBoot 提供了注解 @Async 来使用线程池, 具体使用方法如下:

  1. 在启动类(配置类)添加@EnableAsync来开启线程池
  2. 在需要开启子线程的方法上添加注解 @Async

下面是一个简单的例子:

@Component
@EnableAsync
@EnableScheduling
public class ScheduleTask {

    @Async
    @Scheduled(fixedRate = 2000)
    public void testAsync1() {
        try {
            Thread.sleep(6000);
            System.out.println(LocalDateTime.now() + "--线程1:" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Async
    @Scheduled(cron = "*/2 * * * * ?")
    public void testAsync2() {
        try {
            Thread.sleep(1000);
            System.out.println(LocalDateTime.now() + "--线程2:" + Thread.currentThread().getName());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

启动项目,得到如下日志结果:

图片

可以发现在当前环境下 task-${id} 这个 id 并不是一直增长的,而是一直在复用 1-8。这个时候可能就会有的小伙伴们会比较好奇,默认的不是 SimpleAsyncTaskExecutor 吗?为什么从日志打印的效果上看像是一直在复用 8 个线程,难道用的是 ThreadPoolTaskExecutor?

原因是 SpringBoot2.1.0 版本后,新增了 TaskExecutionAutoConfiguration 配置类。其中声明的默认线程池就是 ThreadPoolTaskExecutor 。而 @Async 在选择执行器的时候会先去 IOC 容器中先找是否有 TaskExecutor 的 Bean对象,所以在当前版本 SpringBoot 中,@Async 的默认 TaskExecutor 是 ThreadPoolTaskExecutor。

线程池配置

在 SpringBoot 项目中,我们可以在 yaml 或者 properties 配置文件中配置,或者使用 @Configuration 配置,下面演示配置方法。

  1. 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=test-task-
  1. 配置类中配置
@Bean(name = "myThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor getMyThreadPoolTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    int i = Runtime.getRuntime().availableProcessors();
    taskExecutor.setCorePoolSize(i * 2);
    taskExecutor.setMaxPoolSize(i * 2);
    taskExecutor.setQueueCapacity(i * 2 * 100);
    taskExecutor.setKeepAliveSeconds(60);
    taskExecutor.setThreadNamePrefix("my-task-");
    taskExecutor.initialize();
    return taskExecutor;
}

拒绝策略

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

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

处理流程

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

使用注意

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

总结

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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 文件
    +关注

    关注

    1

    文章

    566

    浏览量

    24737
  • 容器
    +关注

    关注

    0

    文章

    495

    浏览量

    22061
  • spring
    +关注

    关注

    0

    文章

    340

    浏览量

    14341
  • 线程池
    +关注

    关注

    0

    文章

    57

    浏览量

    6846
收藏 人收藏

    评论

    相关推荐

    跨平台的线程组件--TP组件

    /销毁代价是很高的。那么我们要怎么去设计多线程编程呢???答案:对于长驻的线程,我们可以创建独立的线程去执行。但是非长驻的线程,我们可以通过线程
    的头像 发表于 04-06 15:39 874次阅读

    Java中的线程包括哪些

    线程是用来统一管理线程的,在 Java 中创建和销毁线程都是一件消耗资源的事情,线程可以重复
    的头像 发表于 10-11 15:33 816次阅读
    Java中的<b class='flag-5'>线程</b><b class='flag-5'>池</b>包括哪些

    线程是如何实现的

    线程的概念是什么?线程是如何实现的?
    发表于 02-28 06:20

    基于线程技术集群接入点的应用研究

    本文在深入研究高级线程技术的基础上,分析、研究了固定线程数目的线程线程数目动态变化的
    发表于 01-22 14:21 5次下载

    如何正确使用SpringBoot中的线程

    来自丨CSDN https://blog.csdn.net/m0_37701381/article/details/81072774 使用步骤 先创建一个线程的配置,让Spring Boot加载
    的头像 发表于 09-02 17:14 2090次阅读

    基于Nacos的简单动态化线程实现

    本文以Nacos作为服务配置中心,以修改线程核心线程数、最大线程数为例,实现一个简单的动态化线程
    发表于 01-06 14:14 863次阅读

    线程线程

    线程通常用于服务器应用程序。 每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会占用主线程,也不会延迟后续请求的处理
    的头像 发表于 02-28 09:53 793次阅读
    多<b class='flag-5'>线程</b>之<b class='flag-5'>线程</b><b class='flag-5'>池</b>

    Java线程核心原理

    看过Java线程源码的小伙伴都知道,在Java线程池中最核心的类就是ThreadPoolExecutor,
    的头像 发表于 04-21 10:24 856次阅读

    细数线程的10个坑

    JDK开发者提供了线程的实现类,我们基于Executors组件,就可以快速创建一个线程
    的头像 发表于 06-16 10:11 727次阅读
    细数<b class='flag-5'>线程</b><b class='flag-5'>池</b>的10个坑

    线程线程怎么释放

    线程分组看,pool名开头线程占616条,而且waiting状态也是616条,这个点就非常可疑了,我断定就是这个pool开头线程导致的问题。我们先排查为何这个
    发表于 07-31 10:49 2278次阅读
    <b class='flag-5'>线程</b><b class='flag-5'>池</b>的<b class='flag-5'>线程</b>怎么释放

    线程基本概念与原理

    一、线程基本概念与原理 1.1 线程概念及优势 C++线程简介
    的头像 发表于 11-10 10:24 537次阅读

    线程的基本概念

    线程的基本概念 不管线程是什么东西!但是我们必须知道线程被搞出来的目的就是:提高程序执行效
    的头像 发表于 11-10 16:37 526次阅读
    <b class='flag-5'>线程</b><b class='flag-5'>池</b>的基本概念

    线程七大核心参数执行顺序

    线程是一种用于管理和调度线程执行的技术,通过将任务分配到线程池中的线程进行处理,可以有效地控制并发线程
    的头像 发表于 12-04 16:45 1061次阅读

    线程的创建方式有几种

    线程是一种用于管理和调度线程的技术,能够有效地提高系统的性能和资源利用率。它通过预先创建一组线程并维护一个工作队列,将任务提交给线程
    的头像 发表于 12-04 16:52 866次阅读

    什么是动态线程?动态线程的简单实现思路

    因此,动态可监控线程一种针对以上痛点开发的线程管理工具。主要可实现功能有:提供对 Spring 应用内
    的头像 发表于 02-28 10:42 644次阅读