Spring中的ThreadPoolTaskExecutor线程池使用详解

 更新时间:2024年01月31日 10:19:17   作者:卷不动躺不平的粥  
这篇文章主要介绍了Spring中的ThreadPoolTaskExecutor线程池使用详解,ThreadPoolTaskExecutor 是 Spring框架提供的一个线程池实现,用于管理和执行多线程任务,它是TaskExecutor接口的实现,提供了在 Spring 应用程序中创建和配置线程池的便捷方式,需要的朋友可以参考下

一、配置

1.1 将 ThreadPoolTaskExecutor 注入到 spring 容器内

@Configuration
public class ThreadTaskPoolExecutorConfiguration {
    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数
        taskExecutor.setCorePoolSize(5);
        // 最大线程数
        taskExecutor.setMaxPoolSize(15);
        // 队列大小 默认使用LinkedBlockingQueue
        taskExecutor.setQueueCapacity(100);
        // 线程最大空闲时间
        taskExecutor.setKeepAliveSeconds(300);
        // 拒绝策略 默认new ThreadPoolExecutor.AbortPolicy()
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 线程名称前缀
        taskExecutor.setThreadNamePrefix("My-Task-Executor-");
        //交给spring托管的会自动初始化,因为实现了InitializingBean接口
        // taskExecutor.initialize();
        return taskExecutor;
    }
}

1.2 拒绝策略配置

rejectedExecutionHandler 字段用于配置拒绝策略,常用的拒绝策略如下:

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

其他说明:

  • 为了实现某些特殊的业务需求,用户可以选择使用自定义策略,只需实现RejectedExecutionHandler接口即可。
  • 建议配置threadNamePrefix属性,出问题时可以更方便的进行排查。

1.3 配置线程池个数

  • 如果是 CPU 密集型任务,那么线程池的线程个数应该尽量少一些,一般为 CPU 的个数+1条线程。
  • 如果是 IO 密集型任务,那么线程池的线程可以放的很大,如 2*CPU 的个数。
  • 对于混合型任务,如果可以拆分的话,通过拆分成 CPU 密集型和 IO 密集型两种来提高执行效率;如果不能拆分的的话就可以根据实际情况来调整线程池中线程的个数

二、处理流程

  1. 当一个任务被提交到线程池时,首先查看线程池的核心线程是否都在执行任务,否就选择一条线程执行任务,是就执行第二步。
  2. 查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第三步。
  3. 查看任务队列是否已满,不满就将任务存储在任务队列中,否则执行第四步。
  4. 查看线程池是否已满,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。

在 ThreadPoolExecutor 中表现为:

  • 如果当前运行的线程数小于corePoolSize,那么就创建线程来执行任务(执行时需要获取全局锁)。
  • 如果运行的线程大于或等于corePoolSize,那么就把task加入BlockQueue。
  • 如果创建的线程数量大于BlockQueue的最大容量,那么创建新线程来执行该任务。
  • 如果创建线程导致当前运行的线程数超过maximumPoolSize,就根据饱和策略来拒绝该任务。

三、关闭线程池

调用shutdown或者shutdownNow,两者都不会接受新的任务,而且通过调用要停止线程的interrupt方法来中断线程,有可能线程永远不会被中断,不同之处在于shutdownNow会首先将线程池的状态设置为STOP,然后尝试停止所有线程(有可能导致部分任务没有执行完)然后返回未执行任务的列表。而shutdown则只是将线程池的状态设置为shutdown,然后中断所有没有执行任务的线程,并将剩余的任务执行完。

四、监控线程池状态

常用状态:

  • taskCount:线程需要执行的任务个数。
  • completedTaskCount:线程池在运行过程中已完成的任务数。
  • largestPoolSize:线程池曾经创建过的最大线程数量。
  • getPoolSize获取当前线程池的线程数量。
  • getActiveCount:获取活动的线程的数量

通过继承线程池,重写beforeExecute,afterExecute 和 terminated 方法来在线程执行任务前,线程执行任务结束,和线程终结前获取线程的运行情况,根据具体情况调整线程池的线程数量。

五、实战

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class TestApplicationTests {
    // 注入ThreadPoolTaskExecutor
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Test
    public void ThreadTest(){
        System.out.println(threadPoolTaskExecutor);
        System.out.println("new Runnable()");
        // 创建并执行线程,方式一
        threadPoolTaskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("new Runnable()"+i+"当前线程"+Thread.currentThread().getName());
                }
            }
        });
        // // 创建并执行线程,方式二
        System.out.println("lambda");
        threadPoolTaskExecutor.execute(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println("lambda"+i+"当前线程"+Thread.currentThread().getName());
            }
        });
    }
}

运行截图:

用lambda表达式实现Runnable

我开始使用Java 8时,首先做的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例。

看一下Java 8之前的runnable实现方法,需要4行代码,而使用lambda表达式只需要一行代码。我们在这里做了什么呢?那就是用() -> {}代码块替代了整个匿名类。

// Java 8之前:
new Thread(new Runnable() {
 @Override
 public void run() {
 System.out.println("Before Java8, too much code for too little to do");
 }
}).start();
//Java 8方式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();

到此这篇关于Spring中的ThreadPoolTaskExecutor线程池使用详解的文章就介绍到这了,更多相关ThreadPoolTaskExecutor线程池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java对于JSON的解析方法举例详解

    java对于JSON的解析方法举例详解

    在编写应用时,我们经常要解析JSON,下面这篇文章主要给大家介绍了关于java对于JSON的解析方法,文中通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-09-09
  • IDEA生成项目maven-tree依赖目录树结构方式

    IDEA生成项目maven-tree依赖目录树结构方式

    这篇文章主要介绍了IDEA生成项目maven-tree依赖目录树结构方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • spring mvc4中相关注解的详细讲解教程

    spring mvc4中相关注解的详细讲解教程

    这篇文章主要给大家介绍了关于spring mvc4中相关注解的相关资料,其中详细介绍了关于@Controller、@RequestMapping、@RathVariable、@RequestParam及@RequestBody等等注解的相关内容,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-06-06
  • skywalking分布式服务调用链路追踪APM应用监控

    skywalking分布式服务调用链路追踪APM应用监控

    这篇文章主要为大家介绍了skywalking分布式服务调用链路追踪APM应用监控的功能使用说明,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-03-03
  • Atomikos + MybatisPlus解决多数据源事务一致性问题解决

    Atomikos + MybatisPlus解决多数据源事务一致性问题解决

    在实际项目的开发过程中,我们经常会遇到在同一个项目或微服务中牵涉到使用两个或多个数据源的,本文主要介绍了Atomikos + MybatisPlus解决多数据源事务一致性问题解决,具有一定的参考价值,感兴趣的可以了解一下
    2024-07-07
  • java实现文件重命名

    java实现文件重命名

    这篇文章主要为大家详细介绍了java实现文件重命名,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • SpringBoot整合之SpringBoot整合MongoDB的详细步骤

    SpringBoot整合之SpringBoot整合MongoDB的详细步骤

    这篇文章主要介绍了SpringBoot整合之SpringBoot整合MongoDB的详细步骤,本文通过图文实例代码相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • SpringBoot后端解决跨域问题的3种方案分享

    SpringBoot后端解决跨域问题的3种方案分享

    这篇文章主要给大家分享介绍了关于SpringBoot后端解决跨域问题的3种方案,跨域指的是浏览器不能执行其他网站的脚本,它是由浏览器的同源策略造成的,是浏览器施加的安全限制,需要的朋友可以参考下
    2023-07-07
  • 分布式系统下调用链追踪技术面试题

    分布式系统下调用链追踪技术面试题

    这篇文章主要为大家介绍了分布式系统下调用链追踪技术面试问题合集,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-03-03
  • netty服务端辅助类ServerBootstrap创建逻辑分析

    netty服务端辅助类ServerBootstrap创建逻辑分析

    这篇文章主要介绍了netty服务端辅助类ServerBootstrap创建逻辑分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03

最新评论