SpringBoot为异步任务规划线程池及实现定时任务

上一篇文章中我们学会了如何使用异步的方式去执行任务,在实际的开发当中,应用服务的并发量比较大时,频繁的创建和销毁线程是非常消耗性能和资源的,并且一个进程能够创建的线程数量也是有上限的。为了解决这些问题,我们需要使用线程池来管理这些业务线程。如果没有配置线程池,springboot会自动配置一个ThreadPoolTaskExecutor线程池到bean当中。spring:  task:      execution:        pool:          # 核心线程数          core-size: 8          # 最大线程数          max-size: 16          # 空闲线程存活时间          keep-alive: 60s          # 是否允许核心线程超时          allow-core-thread-timeout: true          # 线程队列数量          queue-capacity: 100        shutdown:          # 关闭等待          await-termination: false          await-termination-period:        # 前缀名称        thread-name-prefix: task-1234567891011121314151617181920复制代码类型:[java]    自定义线程池有时候我们希望将线程放到不同的线程池进行分类,或者有一些个性化的需求。这时我们就可以创建一个线程池配置类并配置一个任务线程池对象。package com.example.demo.configuration;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;import java.util.concurrent.ThreadPoolExecutor;@Configurationpublic class TaskConfiguration {    @Bean("taskExecutor")    public Executor taskExecutor() {        // 创建线程池        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        // 核心线程数、线程池创建时候初始化的线程数,最小线程数        executor.setCorePoolSize(10);        // 线程池最大的线程数(只有在缓冲队列满了之后,才会申请超过核心线程数的线程)        executor.setMaxPoolSize(20);        // 用来缓冲执行任务的队列        executor.setQueueCapacity(200);        // 超过了核心线程之外的线程,在空闲时间到达之后,没活干的线程会被销毁        executor.setKeepAliveSeconds(60);        // 定位处理任务所在的线程池        executor.setThreadNamePrefix("taskExecutor-");        // 线程池对任务的Reject策略,当线程池运行饱和,或者线程池处于shutdown临界状态时,用来拒绝一个任务的执行        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        return executor;    }}123456789101112131415161718192021222324252627282930复制代码类型:[java]    注释中提到的Reject策略一共有四种:AbortPolicy将抛出RejectedExecutionExceptionCallerRunsPolicy直接在execute方法的调用线程中运行被拒绝的任务DiscardOldestPolicy放弃最旧的未处理请求,然后重试executeDiscardPolicy默认情况下它将丢弃被拒绝的任务创建AsyncExecutorTask类继承TaskMethodProvider,@Async注解需要指定前面配置的线程池的名称taskExecutor:package com.example.demo.task;import org.springframework.scheduling.annotation.Async;import org.springframework.scheduling.annotation.AsyncResult;import org.springframework.stereotype.Component;@Componentpublic class AsyncExecutorTask extends TaskMethodProvider {    @Async("taskExecutor")    public void doTaskOneCallback() throws Exception {        super.taskOne();        System.out.println("任务一,当前线程:" + Thread.currentThread().getName());        new AsyncResult<>("任务一完成");    }    @Async("taskExecutor")    public void doTaskTwoCallback() throws Exception {        super.taskTwo();        System.out.println("任务二,当前线程:" + Thread.currentThread().getName());        new AsyncResult<>("任务二完成");    }    @Async("taskExecutor")    public void doTaskThreeCallback() throws Exception {        super.taskThree();        System.out.println("任务三,当前线程:" + Thread.currentThread().getName());        new AsyncResult<>("任务三完成");    }}123456789101112131415161718192021222324252627282930复制代码类型:[java]    编写单元测试:package com.example.demo;import com.example.demo.task.AsyncExecutorTask;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import static java.lang.Thread.sleep;@SpringBootTestpublic class Task {    @Autowired    private AsyncExecutorTask task;    @Test    public void testAsyncExecutorTask() throws Exception {        task.doTaskOneCallback();        task.doTaskTwoCallback();        task.doTaskThreeCallback();        sleep(10 * 1000L);    }}1234567891011121314151617181920212223复制代码类型:[java]    执行单元测试:线程池成功执行异步任务。关闭线程池在原有TaskConfiguration.java代码的基础上添加:executor.setWaitForTasksToCompleteOnShutdown(true);executor.setAwaitTerminationSeconds(60);12复制代码类型:[java]    setWaitForTasksToCompleteOnShutdown(true):线程池关闭的时候等待所有任务都完成后,再继续销毁其他的Bean,使异步任务的销毁就会先于数据库连接池对象的销毁。setAwaitTerminationSeconds(60):设置线程任务等待时间,超过这个时间任务还没有销毁就强制销毁。@Scheduled实现定时任务@Scheduled实现定时任务是SpringBoot自身提供的功能,不需要maven依赖,只需要在启动类上添加@EnableScheduling注解,即可开启定时任务。下面来实现一个定时任务,在task文件夹下创建ScheduledTask.java:package com.example.demo.task;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;import java.util.Date;@Componentpublic class ScheduledTask {    // 方法执行完成后3秒再开始执行    @Scheduled(fixedDelay = 3000)    public void fixedDelayJob() throws InterruptedException {        System.out.println("fixedDelay 开始:" + new Date());        Thread.sleep(10 * 1000);        System.out.println("fixedDelay 结束:" + new Date());    }    // 每隔2秒    @Scheduled(fixedRate = 2000)    public void fixedRateJob() throws InterruptedException {        System.out.println("===========fixedRate 开始:" + new Date());        Thread.sleep(5 * 1000);        System.out.println("===========fixedRate 结束:" + new Date());    }    // 每隔7秒执行一次    @Scheduled(cron = "0/7 * * * * ? ")    public void cronJob() {        System.out.println("=========================== ...>>cron...." + new Date());    }}12345678910111213141516171819202122232425262728293031复制代码类型:[java]    如果只是这样编写所有的定时任务使用的都是一个线程,不能得到我们想要的结果,所以需要解决解决定时任务单线程运行的问题。在config文件夹下创建ScheduleConfig.java:package com.example.demo.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.scheduling.annotation.SchedulingConfigurer;import org.springframework.scheduling.config.ScheduledTaskRegistrar;import java.util.concurrent.Executor;import java.util.concurrent.Executors;@Configuration@EnableSchedulingpublic class ScheduleConfig implements SchedulingConfigurer {    @Override    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {        taskRegistrar.setScheduler(scheduledTaskExecutor());    }    @Bean    public Executor scheduledTaskExecutor() {        // 线程池的大小为3        return Executors.newScheduledThreadPool(3);    }}1234567891011121314151617181920212223242526复制代码类型:[java]    执行代码,得到打印信息:在@Scheduled标签后面括号中的fixedDelay和fixedRate单位都是毫秒,区别是fixedDelay任务执行完毕后一段时间再次执行而fixedRate则是每隔多长时间就执行一次。@Scheduled标签中还可以使用cron表达式:第一位秒(0-59)第二位分(0-59)第三位小时(0-23)第四位日(1-31)第五位月份(1-12)第六位星期几(1-7)第七位年(1970-2099,也可以为空)cron特殊符号:*每秒,每分,每天,每月,每年..?出现在日期和星期这两个位置-表达一个范围,表达一个列表值/x/y,x是开始值,y是步长(0/3,0秒开始,每3秒...)

(0)

相关推荐

  • Redis锁相关

    Redis锁相关 Redis锁相关 君不见,高堂明镜悲白发,朝如青丝暮成雪. 背景:面试的时候被问到有哪些锁,很快脱口而出Volatile.Synchronized和ReentrantLock,也能讲 ...

  • 你不知道的Scheduled定时任务骚操作

    目录 一.什么是定时任务 二.项目依赖 三.注解式定时任务 3.1 cron 3.2 fixedDelay 3.3 fixedDelayString 3.4 fixedRate 3.5 fixedRa ...

  • SpringMVC常用注解详解

    常用注解 RequestParam 作用:把请求中指定名称的参数给控制器中的形参赋值. 属性: value:请求参数的名称 required:请求参数中是否必须提供此参数,默认值为true表示必须提供 ...

  • 你可能写了个假异步,并不能提高请求线程池的吞吐量

    不知道用什么词形容,就叫它假异步吧. 写异步方法,async 和 await 要一路写到底,否则就是假异步,并不能提高请求线程池的吞吐量. 真正的异步,我的理解是这样的:比如调用一个查询接口,在当前线 ...

  • SpringBoot 线程池,也太好用了叭!

    前言 前两天做项目的时候,想提高一下插入表的性能优化,因为是两张表,先插旧的表,紧接着插新的表,一万多条数据就有点慢了 后面就想到了线程池ThreadPoolExecutor,而用的是Spring B ...

  • 如何合理地估算线程池大小?

    这个问题虽然看起来很小,却并不那么容易回答. 大家如果有更好的方法欢迎赐教,先来一个天真的估算方法: 假设要求一个系统的TPS(Transaction Per Second或者Task Per Sec ...

  • C#线程学习笔记三:线程池中的I/O线程

    本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/20/MultiThreads.html,记录一下学习过程以备后续查用.     一.I/O线 ...

  • 多线程之旅(ThreadPool 线程池)

    一.什么是ThreadPool 线程池(源码) 1.线程池顾名思义,有我们的系统创建一个容器装载着我们的线程,由CLR控制的所有AppDomain共享.线程池可用于执行任务.发送工作项.处理异步 I/ ...

  • Java主线程等待子线程、线程池

    print public class TestThread extends Thread { public void run() { System.out.println(this.getName() ...

  • 线程池ThreadPoolExecutor源码分析,看这一篇就够了

    前言 多线程是我们日常工作中很少能接触到的技术,但是面试的时候100%会被问到,万一工作中用到了基本不会,本篇咱们就来深入分析线程池的实现类ThreadPoolExecutor. 1.构造方法 构造方 ...

  • 分析源码,学会正确使用 Java 线程池

    在日常的开发工作当中,线程池往往承载着一个应用中最重要的业务逻辑,因此我们有必要更多地去关注线程池的执行情况,包括异常的处理和分析等.本文主要聚焦在如何正确使用线程池上,以及提供一些实用的建议.文中会 ...

  • 5000字、12 连环炮、一张图快速搞定线程池

    回复"000"获取大量电子书 写在前面 前面文章中,我们总结了JVM18连环炮.并发并最基础的12连环炮,建议先阅读: 连环炮继续走起,今天我给大家总结了线程池的12连环炮. 1. ...