打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
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
将抛出RejectedExecutionException
CallerRunsPolicy
直接在execute方法的调用线程中运行被拒绝的任务
DiscardOldestPolicy
放弃最旧的未处理请求,然后重试execute
DiscardPolicy
默认情况下它将丢弃被拒绝的任务
创建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秒...)
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
修改定时任务不重启项目,SpringBoot如何实现?
java shedule配置自己的线程池
IdGenerator.java
springboot整合Quartz实现动态配置定时任务
你不知道的Scheduled定时任务骚操作
定时任务执行
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服