Springboot学习记录14 异步调用:@Scheduled和@Async
yuyutoo 2024-10-12 00:06 10 浏览 0 评论
因为完全没有接触过Springboot的异步调用,所以本学习记录的代码以及说明,大部分参考自gitee码云的如下工程。鸣谢!
https://gitee.com/didispace/SpringBoot-Learning.git
本学习记录的示例代码克隆地址,分支为develop
https://gitee.com/kutilion/MyArtifactForEffectiveJava.git
本章示例代码结构
- studySpringboot.n14.async.config.ThreadPoolConfig.java 线程池的定义类
- studySpringboot.n14.async.task.CustomizedTask.java 使用线程池的自定义任务类
- studySpringboot.n14.async.task.ScheduledTask.java 定时执行的任务类
- studySpringboot.n14.async.task.AsyncTest.java 测试类,测试自定义任务类
定时任务@Scheduled
想让Springboot支持定时任务,只需要如下两个步骤:
1 在Springboot的启动类中加上@EnableScheduling注解,如:
BootApplication.java
@SpringBootApplication // Add Swagger2 tool. Use http://localhost:8080/swagger-ui.html to access and debug restful api. @EnableSwagger2 @EnableCaching @EnableScheduling public class BootApplication { public static void main(String[] args) { // ApplicationContext context = SpringApplication.run(BootApplication.class, args); SpringApplication sa = new SpringApplication(BootApplication.class); //...
2 在想定时执行的的方法上使用@Scheduled注解。下面方法的注解的属性fixedRate表示每5000毫秒执行一次。主要功能就是每5秒打印一次系统时间。注释部分是它调用的自定义任务类CustomizedTask的doTaskX方法。感兴趣可以打开注释试一下。
ScheduledTask.java
@Scheduled(fixedRate = 5000) public void reportCurrentTime() throws Exception { LocalDateTime ldt = LocalDateTime.now(); String formatedLDT = dtf.format(ldt); System.out.println(formatedLDT); // customizedTask.doTask1(formatedLDT); // customizedTask.doTask2(formatedLDT); // customizedTask.doTask3(formatedLDT); }
启动Springboot服务,可以看到如下结果。发现一个比较有意思的事情, 就是定时执行的方法居然在Springboot application stated event之前执行。
@Scheduled的其他属性
- @Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行
- @Scheduled(fixedDelay = 5000) :上一次执行完毕时间点之后5秒再执行
- @Scheduled(initialDelay=1000, fixedRate=5000) :第一次延迟1秒后执行
- @Scheduled(cron="*/5 * * * * *") :通过cron表达式定义规则
关于cron表达式,可以参照如下连接:
https://www.cnblogs.com/javahr/p/8318728.html
使用线程池的自定义非同步任务
定义线程池
ThreadPoolConfig.java
@Configuration @EnableAsync public class ThreadPoolConfig { @Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 核心线程数 executor.setCorePoolSize(10); // 最大线程数 executor.setMaxPoolSize(20); // 缓冲执行任务的队列长度 executor.setQueueCapacity(200); // 允许线程的空闲时间,超过了核心线程出之外的线程在空闲时间到达之后会被销毁 executor.setKeepAliveSeconds(60); // 线程池名的前缀,输出log的时候比较有用 executor.setThreadNamePrefix("taskExecutor-"); // 线程池对拒绝任务的处理策略,这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样异步任务的销毁就会先于Redis线程池的销毁。 executor.setWaitForTasksToCompleteOnShutdown(true); // 该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁 executor.setAwaitTerminationSeconds(60); return executor; }
线程池的Bean名字为taskExecutor。这个可以自定义,需要从线程池中取得线程的方法会用到这个名字。
使用@Async和线程池实现非同步任务
CustomizedTask.java
@Autowired private StringRedisTemplate stringRedisTemplate; @Async("taskExecutor") public void doTask1(String timing) throws Exception { logger.info("Mission 1 started index : " + timing); long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(10000)); logger.info("Mission 1 radom rediskey: " + stringRedisTemplate.randomKey()); long end = System.currentTimeMillis(); logger.info("Mission 1 completed. cost " + (end - start) + "ms index : " + timing); }
这个类注入了redis的StringRedisTemplate,在任务中会使用到。主要是为了测试程序关闭了,但是任务没有执行完毕,并且使用程序提供的外部资源的情况。redis的方法stringRedisTemplate.randomKey会从redis缓存中随机抽取key值,所以要保证redis中有一些缓存好的数据,否则抽出的是null。可以通过redis-cli实现set一些键值对来用。
@Async注解是本章的核心,它表明标注的方法是可以非同步执行的方法。它的参数taskExecutor是线程池的名字。表明这个方法启动时需要从线程池中取得未被使用的线程。
测试
AsyncTest.java类的test1方法
@Test public void test1() throws Exception { for (int i = 0; i < 100; i++) { if (i == 99) { System.exit(0); } customizedTask.doTask1(Integer.toString(i)); customizedTask.doTask2(Integer.toString(i)); customizedTask.doTask3(Integer.toString(i)); } }
测试结果如图:
程序会从i=0一直执行到i=99时推出。不会出现任何异常。
但是将线程池的如下语句注释
executor.setWaitForTasksToCompleteOnShutdown(true);
当i=99的时候,程序会立即关闭,也就是说redis的连接会马上失效。这个时点没有执行完毕的任务就会抛出异常。
截图过短,异常没有显示出来,全文是
Unexpected exception occurred invoking async method: public void studySpringboot.n14.async.task.CustomizedTask.doTask2(java.lang.String) throws java.lang.Exception java.lang.InterruptedException: sleep interrupted
另外一个需要注意的是for循环的执行顺序是随机的,并不是从1到100。这是因为各个线程争夺资源导致的。for循环本身执行很快,但是它所唤起的任务需要争夺资源,所以会造成任务执行先后顺序的不确定性。
Future类的使用
Future是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果的接口。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
该接口中定义了如下5个方法
boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
以下说明摘自,浅显易懂
http://blog.didispace.com/springbootasync-4/
- cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
- isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
- isDone方法表示任务是否已经完成,若任务完成,则返回true;
- get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
- get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
CustomizedTask.java
该类中定义了返回Future的异步任务
@Async("taskExecutor") public Future<String> doTaskWithFuture() throws Exception { long sleep = random.nextInt(10000); logger.info("Mission 4 started. Needs " + sleep + " ms."); Thread.sleep(sleep); logger.info("Mission 4 completed."); return new AsyncResult<>("This is the return text of doTaskWithFuture."); }
测试方法:
AsyncTest.java
@Test public void test2() throws Exception { Future<String> futureResult = customizedTask.doTaskWithFuture(); String result = futureResult.get(5, TimeUnit.SECONDS); System.out.println(result); }
该测试方法使用了get(long timeout, TimeUnit unit)方法,为异步任务设定了超时时间,5秒。如果任务执行超过了5秒则会抛出异常,如果不足5秒则会正常返回。
异常时的测试结果:
总结
- @Scheduled注解实现定时任务
- @Async注解实现异步任务
- 线程池的简单使用和配置
- Future的简单学习
相关推荐
- 深度解读Spring框架的核心原理
-
深度解读Spring框架的核心原理在Java开发的世界里,提到Spring框架,就像提起一位久经沙场的老将,它几乎成了企业级应用开发的代名词。那么,这个被无数开发者膜拜的框架究竟有何独特之处?今天,我...
- 「Spring认证」Spring 框架概述
-
Spring是最流行的企业Java应用程序开发框架。全球数以百万计的开发人员使用SpringFramework来创建高性能、易于测试和可重用的代码。Spring框架是一个开源的Java...
- 学习Spring框架 这一篇就够了
-
1.spring概述1.1Spring是什么(理解)...
- Spring框架双核解析:IOC与AOP的本质与实战
-
#Spring核心#IOC容器#AOP编程#Java框架设计...
- Spring Boot与传统Spring框架的对比:探索Java开发的新境界
-
SpringBoot与传统Spring框架的对比:探索Java开发的新境界在Java生态系统中,Spring框架无疑是一个里程碑式的存在。从最初的简单依赖注入容器,到如今覆盖企业级开发方方面面的庞大...
- Spring MVC框架源码深度剖析:从入门到精通
-
SpringMVC框架源码深度剖析:从入门到精通SpringMVC框架简介SpringMVC作为Spring框架的一部分,为构建Web应用程序提供了强大且灵活的支持。它遵循MVC(Model-V...
- Spring框架入门
-
一.spring是什么?Spring是分层...
- 程序员必知必会技能之Spring框架基础——面向切面编程!
-
面向切面编程AOP(AspectOrientedProgramming)与OOP(ObjectOrientedProgramming,面向对象编程)相辅相成。AOP提供了与OOP不同的抽象软件结...
- Spring Security安全框架深度解读:为你的应用穿上“钢铁铠甲”
-
SpringSecurity安全框架深度解读:为你的应用穿上“钢铁铠甲”在现代网络世界里,保护我们的应用程序免受各种威胁攻击至关重要。而在这个过程中,SpringSecurity框架无疑是我们最可...
- Spring框架的设计哲学与实现:打造轻量级的企业级Java应用
-
Spring框架的设计哲学与实现:打造轻量级的企业级Java应用Spring框架自2003年诞生以来,已成为企业级Java应用开发的代名词。它不仅仅是一个框架,更是一种设计理念和哲学的体现。本文将带你...
- Spring框架深度解析:从核心原理到底层实现的全方位避坑指南
-
一、Spring框架核心概念解析1.控制反转(IoC)与依赖注入(DI)Spring的核心思想是通过IoC容器管理对象的生命周期和依赖关系。传统开发中,对象通过new主动创建依赖对象,导致高耦合;而S...
- Java框架 —— Spring简介
-
简介一般来说,Spring指的是SpringFramework,它提供了很多功能,例如:控制反转(IOC)、依赖注入...
- Spring 框架概述,模块划分
-
Spring框架以控制反转(InversionofControl,IoC)和面向切面编程(Aspect-OrientedProgramming,AOP)为核心,旨在简化企业级应用开发,使开发者...
- spring框架怎么实现依赖注入?
-
依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入。具体介绍如下:●属性set...
- Spring框架详解
-
Spring是一种开放源码框架,旨在解决企业应用程序开发的复杂性。一个主要优点就是它的分层体系结构,层次结构让你可以选择要用的组件,同时也为J2EE应用程序开发提供了集成框架。 Spring特征...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
推荐7个模板代码和其他游戏源码下载的网址
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)