java 异步定时任务_spring 定时任务 异步调用
本篇仅限于 Spring 定时任务 & 异步调用的基本使用,不涉及深入原理探究,先学会怎么用,再探究原理.
环境:SpringBoot 2.0
定时任务指的是应用程序在指定的时间执行预先定义好的程序片段
在 Spring 中使用定时任务非常简便,分为三步:编写定时任务类并注入到 IOC 容器,一般使用 @Component 注入
编写定时任务方法并使用 @Scheduled 标记,这里需要了解一个叫 Cron 表达式的知识点
在 SpringBoot 启动类上使用 @EnableScheduling 开启定时任务功能
/*** 1、注入到 IOC 容器,定时任务不属于 service 或 controller,一般使用 @Component 标注*/
@Slf4j
@Component
public class HelloScheduled {
/*** 2、编写定时任务方法并使用 @Scheduled 标记* 注意:@Scheduled cron 属性中书写的是 cron 表达式,如下的 cron 表达式表示:每隔5秒执行一次*/
@Scheduled(cron = "*/5 * * * * ?")
public void scheduled() {
log.info("使用 Spring @Scheduled 制定定时任务");
}
}
/*** 3、在 `SpringBoot` 启动类上使用 `@EnableScheduling` 开启定时任务*/
@EnableScheduling
@SpringBootApplication
public class SpringAllApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAllApplication.class, args);
}
}
自定义配置文件
上述代码我们是在SpringBoot启动类上使用@EnableScheduling开启定时任务,而在实际开发我们会使用JavaConfig配置并约束package,使得指定的组件只有在指定package才有效.
/*** 定时任务只扫描 HelloScheduled 类所在的包*/
@EnableScheduling
@Configuration
@ComponentScan(basePackageClasses = com.xbhel.springall.component.scheduled.HelloScheduled.class)
public class ScheduledConfig {
}
异步调用异步调用是相对同步调用而言,同步调用指的是程序按照指令一步步执行,每一步都必须等待上一步执行完成后才可以执行,同步调用具有依赖性,而异步调用无需等待上一步执行完成便可以执行,即异步调用不依赖上一步操作,所有可以并发执行.
在 Spring 中使用异步调用也非常简便,分为三步:编写业务类并注入到IOC容器中
编写异步调用方法并使用@Async标记
在SpringBoot启动类上使用@EnableAsync开启异步调用功能
/*** 1、注入到 IOC 容器*/
@Slf4j
@Service
public class HelloAsync {
/*** 2、编写异步调用方法并使用`@Async`标记*/
@Async
public void async() {
log.info("HelloAsync class Thread Name {}",Thread.currentThread().getName());
Stream.iterate(0, e -> ++e)
.limit(10)
.forEach(e -> log.info("HelloAsync class async method {}", e));
}
}
/*** 3、在 `SpringBoot` 启动类上使用 `@EnableAsync` 开启定时任务*/
@EnableAsync
@SpringBootApplication
public class SpringAllApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAllApplication.class, args);
}
}
获取异步调用结果
在异步调用中,我们并不知道程序何时结束,从而无法获取它的结果,但有些时候我们需要获取异步调用的结果,此时就需要应用到Java提供的Future Interface,它的原理是基于回调机制.
@Async
public Future asyncResult(T t){
Stream.iterate(0, e -> ++e)
.limit(10)
.forEach(e -> log.info("HelloAsync class asyncResult method {},{}", e,t));
//通过AsyncResult 返回结果 return new AsyncResult<>(t);
}
public String testAsync() throws ExecutionException, InterruptedException {
final String info="hello";
Future result = async.asyncResult(info);
//在 async task 结束之前一直阻塞,task 执行完毕并返回结果 return result.get();
}
Future中提供了一系列方法获取异步任务的状态
自定义线程池
默认情况下异步调用使用的线程池是SimpleAsyncTaskExecutor,该线程池是不被推荐,因为该线程池的线程不重用,每次调用都会创建一个新的线程,一般我们都会自定义一个线程池进行服用,同样使用JavaConfig配置并约束package
@EnableAsync
@Configuration
@ComponentScan(basePackageClasses = com.xbhel.springall.component.async.HelloAsync.class)
public class AsyncConfig{
private static final String THREAD_PREFIX = "SpringAll-";
@Bean("asyncExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//线程池名称前缀 executor.setThreadNamePrefix(THREAD_PREFIX);
//线程池的默认线程 executor.setCorePoolSize(3);
//线程池最大线程数 executor.setMaxPoolSize(10);
//线程缓存队列 executor.setQueueCapacity(100);
//线程空闲时间 executor.setKeepAliveSeconds(60);
/*线程池对拒绝任务的处理策略(rejection policy):当线程池已经达到最大线程数量,没有空闲线程时,新任务该如何处理可选策略:CallerRunsPolicy:当线程池没有能力处理时直接在执行方法的调用线程中运行被拒绝的任务如果执行程序已经关闭,将丢弃该任务.AbortPolicy:处理程序遭到拒绝时将抛出 RejectedExecutionException*/
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//等待所有任务调度完成在关闭线程池,保证所有的任务被正确处理 executor.setWaitForTasksToCompleteOnShutdown(true);
//线程池关闭时等待其他任务的时间,不能无限等待,确保应用最后能被关闭。而不是无限期阻塞 executor.setAwaitTerminationSeconds(60);
//线程池初始化 executor.initialize();
return executor;
}
}
此时,只需在原来的异步任务的@Async()注解中指定使用自定义线程池即可
@Async("asyncExecutor")
public void async() {
log.info("HelloAsync class Thread Name {}",Thread.currentThread().getName());
Stream.iterate(0, e -> ++e)
.limit(10)
.forEach(e -> log.info("HelloAsync class async method {}", e));
}
应用程序范围
上述我们需在@Async()注解中指定使用自定义线程池才有效,那有没有应用程序范围的呢,不需要每次指定,默认就会使用自定义线程池呢?
这时可以通过implement AsyncConfigurer 或extend AsyncConfigurerSupport创建应用程序范围的.
@EnableAsync
@Configuration
@ComponentScan(basePackageClasses = com.xbhel.springall.component.async.HelloAsync.class)
public class AsyncConfig extends AsyncConfigurerSupport {
private static final String THREAD_PREFIX = "SpringAll-";
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix(THREAD_PREFIX);
executor.setCorePoolSize(3);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(60);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
此时,我们调用异步任务就默认就会使用自定义线程池了.
异常处理
在异步调用中,当异步任务返回类型为Future时异常处理非常容易,Future.get()方法引发异常在主线程处理即可,当异步任务返回类型为 void,异常不会传播到调用线程,需要通过实现 AsyncUncaughtExceptionHandler Interface来创建自定义异常处理程序.
上述我们在配置应用程序范围的线程池时,AsyncConfigurerSupport提供了两个方法,其中getAsyncExecutor()是定义线程池的,而getAsyncUncaughtExceptionHandler()就是用于处理异常的.
此时只需创建一个实现了AsyncUncaughtExceptionHandler的异常处理器,并在getAsyncUncaughtExceptionHandler()返回该异常处理器的实例即可.
/*** 异步任务的异常处理 class*/
@Slf4j
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable e, Method method, Object... args) {
final String methodName=method.getName();
log.error("Async task : method {} , params {}",methodName,args,e);
}
}
最后,谢谢您能看到这里,希望您能给我点个赞,Thank you!
后续:Spring中使用Quartz定时任务框架~
java 异步定时任务_spring 定时任务 异步调用相关推荐
- Django配置celery执行异步任务和定时任务
原生celery,非djcelery模块,所有演示均基于Django2.0 celery是一个基于python开发的简单.灵活且可靠的分布式任务队列框架,支持使用任务队列的方式在分布式的机器/进程/线 ...
- 八十、 Springboot整合异步任务和定时任务
@Author:Runsen 来源:尚硅谷 下面建议读者学习尚硅谷的B站的SpringBoot视频,我是学雷丰阳视频入门的. 具体链接如下:B站尚硅谷SpringBoot教程 文章目录 任务 异步任务 ...
- Asp-Net-Core开发笔记:集成Hangfire实现异步任务队列和定时任务
1前言 最近把Python写的数据采集平台往.Net Core上迁移,原本的采集任务使用多进程+线程池的方式来加快采集速度,使用Celery作为异步任务队列兼具定时任务功能,这套东西用着还行,但反正就 ...
- Django blog项目《二十五》:项目优化《1》使用celery异步任务和定时任务
celery异步异步任务处理 一.celery简介 celery 官方文档英文版:http://docs.celeryproject.org/en/latest/index.html 组件 任务Tas ...
- JAVA Web Servlet中的异步处理 (2) -- Servlet3.1中的Non-blocking IO支持
JAVA Web Servlet中的异步处理 (2) – Servlet3.1中的Non-blocking IO支持 在servlet 3.1中,新增了non-blocking IO支持. 在serv ...
- JAVA Web Servlet中的异步处理 (1) -- Servlet3.0中的Async支持
JAVA Web Servlet中的异步处理 (1) – Servlet3.0中的Async支持 每个请求来到Web容器,Web容器会为其分配一个线程来专门负责该请求,直到完成处理前,该执行线程都不会 ...
- Java 非阻塞 IO 和异步 IO
转载自 Java 非阻塞 IO 和异步 IO 上一篇文章介绍了 Java NIO 中 Buffer.Channel 和 Selector 的基本操作,主要是一些接口操作,比较简单. 本文将介绍非阻塞 ...
- java 异步_聊聊java高并发系统之异步非阻塞
作者:孙伟,目前负责京东商品详情页统一服务系统,写过java,写过ngx_lua,还写过storm等,喜欢学习研究新事物. 在做电商系统时,流量入口如首页.活动页.商品详情页等系统承载了网站的大部分流 ...
- java登录时启动后台异步线程_JAVA多线程的同步和 异步
原标题:JAVA多线程的同步和 异步 1.多线程和异步操作的异同 多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性.甚至有些时候我们就认为多线程和异步操作是等同的概念.但是 ...
最新文章
- 高效开发:IntelliJ IDEA天天用,这些Debug技巧你都知道?
- 《.Net框架程序设计》读书笔记 之 结构和索引器
- 剪我一根头发,就要做我一天女人。
- ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your
- java锁_Java锁
- JavaFX真实世界应用程序:欧洲电视网广播联盟
- 职业中专计算机基础试讲课,职业中专计算机基础教育分析
- 容器编排技术 -- Kubernetes kubectl delete 命令详解
- cudaMemset的调用方式
- ROS2——Windows上的安装笔记
- BeanUtils组件使用小指南
- JDK帮助文档无法打开的常见问题和解决办法
- 多按键多界面二维数组表驱动设计
- 从深圳流水线女工到美国谷歌程序员-一位女孩的奋斗史诗
- php修改服务器ip地址,php修改服务器ip地址
- LoadRunner性能测试实战教程
- Linux修改和恢复服务器时间
- CAN 常见错误排查
- 电子元器件贸易采购管理常见难题及解决方案
- 时间复杂度分析:递归算法