一篇文章看懂@Scheduled定时器/@Async/CompletableFuture

一篇文章看懂@Scheduled定时器/@Async/CompletableFuture

@Scheduled注解解析:

1.cron:最重要的一个参数

在这里插入图片描述
cron表达式[秒] [分] [小时] [日] [月] [周] [年]([年]可省略)
简单了解一下,网上有现成的工具
在这里插入图片描述

示例:
每隔5秒执行一次:*/5 * * * * ?
每隔1分钟执行一次:0 */1 * * * ?
每天23点执行一次:0 0 23 * * ?

cron表达式使用占位符
另外,cron属性接收的cron表达式支持占位符。eg:
配置文件:

time:cron: */5 * * * * *interval: 5
可通过${}来获取@Scheduled(cron="${time.cron}")void testPlaceholder1() {System.out.println("Execute at " + System.currentTimeMillis());}@Scheduled(cron="*/${time.interval} * * * * *")void testPlaceholder2() {System.out.println("Execute at " + System.currentTimeMillis());}

2.zone

在这里插入图片描述

时区,cron表达式会基于该时区解析。通常默认为空字符串,也就是获取服务器所在地的时区。

3、fixDelay和fixDelayString

在这里插入图片描述

fixDelay 上一次执行完毕时间点之后多长时间再执行
fixDelayString与fixDelay意义一样,不过是字符串的,并且支持占位符。

4、fixRate和fixRateString

在这里插入图片描述

fixRate:上一次开始执行时间点之后多长时间再执行
fixRateString与fixRate意义一样,不过是字符串的,并且支持占位符;

5、initialDelay和initialDelayString

在这里插入图片描述

initialDelay:第一次延迟多长时间后再执行;
initialDelayString与initialDelay意义一样,不过是字符串的,并且支持占位符;

6、使用与避坑

配置方法一:项目启动的时候第一次不会执行定时任务,等到第五秒的时候才会执行;
在这里插入图片描述
配置方法二:项目已启动就开始执行定时任务
在这里插入图片描述

7、定时器默认为单线程,所以如果项目中使用多个定时器要配置线程池 ,通过实现SchedulingConfigurer接口来打到多线程运行定时器的目的

在这里插入图片描述

自定义工厂:
在这里插入图片描述

8、Spring中@Async用法总结

调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。
在项目应用中,@Async调用线程池,推荐使用自定义线程池的模式。自定义线程池常用方案:重新实现接口AsyncConfigurer。

1. 何为异步调用?
在解释异步调用之前,我们先来看同步调用的定义;同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。
2. 常规的异步调用处理方式
在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况
3. @Async介绍

在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。

4. 基于@Async无返回值调用的异常处理,

配置AsyncUncaughtExceptionHandler,通过AsyncConfigurer自定义线程池,以及异常处理;

@Configuration
@EnableAsync
public class SpringAsyncConfiguration implements AsyncConfigurer {private static final Logger logger = LoggerFactory.getLogger(getClass());@Bean@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(8);executor.setMaxPoolSize(16);executor.setQueueCapacity(64);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.setThreadNamePrefix("SpringAsyncThread-");return executor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new SpringAsyncExceptionHandler();}class SpringAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {@Overridepublic void handleUncaughtException(Throwable throwable, Method method, Object... obj) {logger.error("Exception occurs in async method", throwable.getMessage());}}}
5.基于@Async返回值的调用的异常处理:

如果异步方法有返回值,那就应当返回AsyncResult类的对象,以便在调用处捕获异常。因为AsyncResult是Future接口的子类,所以也可以通过future.get()获取返回值的时候捕获ExcecutionException。
异步方法:

@Service
public class AsyncService {@Asyncpublic AsyncResult<String> asyncMethodWithResult() {// do something(可能发生异常)return new AsyncResult("hello");}
}
测试调用:
public class Test {private Logger logger = LoggerFactory.getLogger(getClass());@AutowiredAsyncService asyncService;public void test() {try {Future future = asyncService.asyncMethodWithResult();future.get();} catch (ExecutionException e) {logger.error("exception occurs", e);} catch (InterruptedException e) {logger.error("exception occurs", e);}}}
6、@Async调用中的事务处理机制

在@Async标注的方法,同时也加上了@Transactional的时候;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional.

7、Spring 已经实现的线程池
  1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,因为默认每次调用都会创建一个新的线程。
  2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方。
  3. ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
  4. SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。
  5. ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装。
8、 @Async应用默认线程池

Spring应用默认的线程池,指在@Async注解在使用时,不指定线程池的名称。查看源码,@Async的默认线程池为SimpleAsyncTaskExecutor。

9、有返回值CompletableFuture调用

A、JDK5新增了Future接口,用于描述一个异步计算的结果。虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果。
B、CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段
C、在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。

10、默认线程池的弊端

在线程池应用中,参考阿里巴巴java开发规范:线程池不允许使用Executors去创建,也不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确线程池的运行规则,规避资源耗尽的风险。Executors各个方法的弊端:
A、newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
B、newCachedThreadPool和newScheduledThreadPool:要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM
@Async默认异步配置使用的是SimpleAsyncTaskExecutor,该线程池默认来一个任务创建一个线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误。针对线程创建问题,SimpleAsyncTaskExecutor提供了限流机制,通过concurrencyLimit属性来控制开关,当concurrencyLimit>=0时开启限流机制,默认关闭限流机制即concurrencyLimit=-1,当关闭情况下,会不断创建新的线程来处理任务。基于默认配置,SimpleAsyncTaskExecutor并不是严格意义的线程池,达不到线程复用的功能。

11、@Async应用自定义线程池

自定义线程池,可对系统中线程池更加细粒度的控制,方便调整线程池大小,同时也易于线程执行异常控制和处理。在设置系统自定义线程池代替默认线程池时,虽可通过多种模式设置,但替换默认线程池最终产生的线程池有且只能设置一个(不能设置多个类实现AsyncConfigurer)。自定义线程池有如下模式:
a、重新实现接口AsyncConfigurer
b、继承AsyncConfigurerSupport
c、配置由自定义的TaskExecutor替代内置的任务执行器
通过查看Spring源码关于@Async的默认调用规则,会优先查询源码中实现AsyncConfigurer这个接口的类,实现这个接口的类为AsyncConfigurerSupport。但默认配置的线程池和异步处理方法均为空,所以,无论是继承或者重新实现接口,都需指定一个线程池。且重新实现 public Executor getAsyncExecutor()方法。

A、 实现接口AsyncConfigurer
@Configuration
public class AsyncConfiguration implements AsyncConfigurer {@Bean("kingAsyncExecutor")public ThreadPoolTaskExecutor executor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();int corePoolSize = 10;executor.setCorePoolSize(corePoolSize);int maxPoolSize = 50;executor.setMaxPoolSize(maxPoolSize);int queueCapacity = 10;executor.setQueueCapacity(queueCapacity);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());String threadNamePrefix = "kingDeeAsyncExecutor-";executor.setThreadNamePrefix(threadNamePrefix);executor.setWaitForTasksToCompleteOnShutdown(true);// 使用自定义的跨线程的请求级别线程工厂类RequestContextThreadFactory threadFactory = RequestContextThreadFactory.getDefault();executor.setThreadFactory(threadFactory);int awaitTerminationSeconds = 5;executor.setAwaitTerminationSeconds(awaitTerminationSeconds);executor.initialize();return executor;}@Overridepublic Executor getAsyncExecutor() {return executor();}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return (ex, method, params) -> ErrorLogger.getInstance().log(String.format("执行异步任务'%s'", method), ex);}
}
B、继承AsyncConfigurerSupport
@Configuration  
@EnableAsync  
class SpringAsyncConfigurer extends AsyncConfigurerSupport {  @Bean  public ThreadPoolTaskExecutor asyncExecutor() {  ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();  threadPool.setCorePoolSize(3);  threadPool.setMaxPoolSize(3);  threadPool.setWaitForTasksToCompleteOnShutdown(true);  threadPool.setAwaitTerminationSeconds(60 * 15);  return threadPool;  }  @Override  public Executor getAsyncExecutor() {  return asyncExecutor;  
}  @Override  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return (ex, method, params) -> ErrorLogger.getInstance().log(String.format("执行异步任务'%s'", method), ex);
}
}
12、配置自定义的TaskExecutor

由于AsyncConfigurer的默认线程池在源码中为空,Spring通过beanFactory.getBean(TaskExecutor.class)先查看是否有线程池,未配置时,通过beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class),又查询是否存在默认名称为TaskExecutor的线程池。所以可在项目中,定义名称为TaskExecutor的bean生成一个默认线程池。也可不指定线程池的名称,申明一个线程池,本身底层是基于TaskExecutor.class便可。

 @EnableAsync@Configurationpublic class TaskPoolConfig {@Bean(name = AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME)public Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心线程池大小executor.setCorePoolSize(10);//最大线程数executor.setMaxPoolSize(20);//队列容量executor.setQueueCapacity(200);//活跃时间executor.setKeepAliveSeconds(60);//线程名字前缀executor.setThreadNamePrefix("taskExecutor-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;}      @Bean(name = "new_task")public Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心线程池大小executor.setCorePoolSize(10);//最大线程数executor.setMaxPoolSize(20);//队列容量executor.setQueueCapacity(200);//活跃时间executor.setKeepAliveSeconds(60);//线程名字前缀executor.setThreadNamePrefix("taskExecutor-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;}}
##### 13、 创建CompletableFuture对象
CompletableFuture提供了四个静态方法用来创建CompletableFuture对象:
public static CompletableFuture<Void>   runAsync(Runnable runnable)
public static CompletableFuture<Void>   runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier, Executor executor)Asynsc表示异步,而supplyAsync与runAsync不同在与前者异步返回一个结果,后者是void.第二个函数第二个参数表示是用我们自己创建的线程池,否则采用默认的ForkJoinPool.commonPool()作为它的线程池.其中Supplier是一个函数式接口,代表是一个生成者的意思,传入0个参数,返回一个结果.
CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{return "hello world";});
System.out.println(future.get());  //阻塞的获取结果  ''helllo world"

多个线程池
@Async注解,使用系统默认或者自定义的线程池(代替默认线程池)。可在项目中设置多个线程池,在异步调用时,指明需要调用的线程池名称,如@Async(“new_task”)。
参考博客:https://www.cnblogs.com/wlandwl/p/async.html
Spring使用@Async注解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558809.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一篇搞懂HTTP协议

本文转自 &#xff1a;flyhero 码上实战《一个HTTP打趴80%面试者》 HTTP协议简介 HTTP&#xff08;超文本传输协议&#xff09;是应用层上的一种客户端/服务端模型的通信协议,它由请求和响应构成&#xff0c;且是无状态的。&#xff08;暂不介绍HTTP2&#xff09; 协议&…

Jackson高级操作————节点树

引言 继《Jackson快速入门》基础篇之后的树模型相关操作。 节点树模型 ObjectMapper构建JsonNode节点树&#xff0c;类似于DOM解析器的XML。 Testpublic void testJsonTree() throws JsonProcessingException, IOException {String jsonString "{\"name\":\…

Jackson高级操作————流式API与JsonGenerator、JsonParser

引言 继《Jackson快速入门》之后的高级相关操作。 Jackson提供了一种对于性能要求应用程序操作json更加高效的方式——流式API&#xff0c;这种方式开销小&#xff0c;性能高&#xff0c;因此&#xff0c;如果应用程序或者程序逻辑对于性能有一定要求&#xff0c;可以使用这种…

Mysql高级教程思维导图

Mysql高级教程思维导图 1、思维导图总览&#xff1a; 2、MySQL架构介绍&#xff1a; 2.1、MySQL简介 2.2、MySQL Linux版安装 2.3、MySQL配置文件 2.4、MySQL逻辑架构介绍 2.5、MySQL存储引擎 2.6、MySQL的用户和权限管理 2.7、MySQL一些杂项配置 ’ 3、索引优化分析 3.1、性…

Eclipse(STS 4)闪退导致EGit插件异常

引言 到目前为止&#xff0c;STS已经闪退过三次了。 问题很棘手&#xff0c;我需要冷静。 首先出现了一个问题就是&#xff0c;EGit无法commit。 第二个问题是切换分支报错。 闪退重启后EGit无法提交代码 观察闪退出现的时机&#xff0c;一般出现在我 commit 代码的时候&…

spring源码分析第二天------spring系统概述以及IOC实现原理

1、Spring5 概述 Spring 是一个开源的轻量级 Java SE&#xff08;Java 标准版本&#xff09;/Java EE&#xff08;Java 企业版本&#xff09;开发应用框架&#xff0c; 其目的是用于简化企业级应用程序开发。 Spring 框架除了帮我们管理对象及其依赖关系&#xff0c;还提供像通…

Linux进阶之路————Linux概述

引言 Linux是一个开源、免费的操作系统。学习Linux知识分为以下几个阶段&#xff1a; 第一阶段&#xff1a;Linux环境下的基本操作命令&#xff0c;包括文件操作命令&#xff08;rm、mkdir、chmod、chown&#xff09;编辑工具使用&#xff08;vi、vim&#xff09;Linux用户管…

spring源码分析第三天------spring核心IOC容器和依赖注入原理

基于XML的依赖注入 1、依赖注入发生的时间 当 Spring IOC 容器完成了 Bean 定义资源的定位、载入和解析注册以后&#xff0c;IOC 容器中已经管理类 Bean 定义的相关数据&#xff0c;但是此时 IOC 容器还没有对所管理的 Bean 进行依赖注入&#xff0c;依赖注入在以下两种情况 …

JavaWeb中监听器Listener+过滤器filter+拦截器interceptor区别

JavaWeb中监听器Listener过滤器filter拦截器interceptor区别 如果从整个项目中看&#xff0c;一个servlet请求的执行过程就变成了这样context-param–>listener–>filter–>servlet–>interceptor(指的是拦截器) 1.概念 context-param&#xff1a;就是一些需要初…

Linux进阶之路————开机、重启和用户登录注销

关机、重启命令 shutdown 命令&#xff1a; 1&#xff09;shutdown -h now 立即关机 2&#xff09;shutdown -h 1 1分钟之后关机 3&#xff09;shutdown -r now 立即重启 reboot 命令&#xff0c;直接使用&#xff0c;表示立即重启 halt 命令&am…

spring源码分析第四天------springmvc核心原理及源码分析

spring源码分析第四天------springmvc核心原理及源码分析 1、基础知识普及 2、 SpringMVC请求流程 3、SpringMVC代码流程 4、springMVC源码分析 4.1 4.2 4.3 5、Spring MVC 的优化 补充&#xff1a; SpringMVC经典设计是handlerMapping&#xff0c;大家可以看一…

Linux进阶之路————用户管理

引言 前面几篇关于Linux的已经大概领略了Linux的风采&#xff0c;本篇用户管理&#xff0c;将着重总结日常工作中&#xff0c;非常重要的用户管理功能。 主要包括&#xff1a;新增用户、删除用户、查询用户信息、指定/修改密码、切换用户、用户组&#xff0c;以及与用户管理相…

spring源码分析第五天------springAOP核心原理及源码分析

spring源码分析第五天------springAOP核心原理及源码分析 1、 面向切面编程。可以通过预 编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术 切面&#xff08;Aspect&#xff09; 连接点&#xff08;Joinpoint&#xff09; 通知&#x…

Linux进阶之路————Linux运行级别(重置密码)

引言 前面的《Linux进阶之路————开机、重启和用户登录注销》已经简单介绍过Linux系统运行级别的概念&#xff0c;今天来详细介绍和总结一下Linux的运行级别。 inittab配置文件 在配置文件目录下有一个inittab文件/etc/inittab&#xff0c;用于专门存储系统的运行级别&am…

Linux问题处理————命令提示符显示 bash-4.1# 解决方案

引言 本文系Linux学习过程中的一个小插曲。 在使用root用户创建了一个普通的tom 用户之后&#xff0c;系统莫名其妙卡死&#xff0c;重启后&#xff0c;发现命令提示符不再是 “用户名 目录”这种组合&#xff0c;而变成了 bash-4.1# 开头&#xff1a; 解决办法 将/etc/ske…

spring源码分析第六天------spring经典面试问题

spring源码分析第六天------spring经典面试问题 1、Spring5 新特性及应用举例 2、Spring 经典的面试问题 a、什么是 Spring 框架&#xff1f;Spring 框架有哪些主要模块&#xff1f; Spring 框架是一个为 Java 应用程序的开发提供了综合、广泛的基础性支持的 Java 平台。Spr…

Linux进阶之路————scp指令介绍与演示

引言 目前市场上很多终端软件都自带文件传输功能&#xff0c;且多为图形化界面&#xff0c;操作简单无脑。但是本着“指令最高端&#xff0c;拖拽贼JBLow”的原则&#xff0c;今天学习一下文件传输的命令&#xff1a;scp 操作演示 准备两台linux服务器 &#xff08;我的是弄…

Mybatis源码分析第一天------Mybatis实用篇

Mybatis源码分析第一天------Mybatis实用篇 一切最基本的操作就是参考官方文档&#xff1a;https://mybatis.org/mybatis-3/zh/configuration.html 1、是什么 什么是 MyBatis&#xff1f; a、MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级…

深入理解Tomcat和Jetty源码之第一篇前奏知识

深入理解Tomcat和Jetty源码之第一篇前奏知识 这周开始对Tomcat和Jetty的源码和框架产生了浓厚的兴趣&#xff0c;接下来想记录一下学习Tomcat和Jetty源码的心得和体会&#xff0c;有兴趣的朋友可以关注一下&#xff0c;我主要以思维导图的方式来展现整体的学习思路~ 今天的整体…

Linux进阶之路————组管理与权限管理

引言 这篇博客将会总结一些关于组的概念和相关操作&#xff0c;以及文件的一些权限管理。 一、组的基本介绍 在前面的博文《Linux进阶之路————用户管理》已经有提到用户组的概念&#xff0c;实际上&#xff0c;组的概念并不局限于用户&#xff0c;文件和目录都有组的概念…