一篇文章看懂@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注解

一篇文章看懂@Scheduled定时器/@Async/CompletableFuture相关推荐

  1. c++ socket线程池原理_一篇文章看懂 ThreadLocal 原理,内存泄露,缺点以及线程池复用的值传递问题...

    编辑:业余草来源:https://www.xttblog.com/?p=4946 一篇文章看懂 ThreadLocal 原理,内存泄露,缺点以及线程池复用的值传递问题. ThreadLocal 相信不 ...

  2. 一篇文章看懂MySQL的多表连接(包含左/右/全外连接)

    MySQL的多表查询 这是第二次学习多表查询,关于左右连接还是不是很熟悉,因此重新看一下.小目标:一篇文章看懂多表查询!! 这篇博客是跟着宋红康老师学习的,点击此处查看视频,关于数据库我放在了Gith ...

  3. 一篇文章看懂:量化交易

    什么是量化交易? 度娘官方版 - 理论这么说 量化交易是指以先进的数学模型替代人为的主观判断,利用计算机技术从庞大的历史数据中海选能带来超额收益的多种"大概率"事件以制定策略,极大 ...

  4. 一篇文章看懂变量提升(hoisting)

    文章转自:一篇文章看懂变量提升(hoisting) - 知乎 前沿 大家对变量提升(hoisting)一定不陌生了,这篇文章希望可以建立一个系统的规则,以后碰到变量提升,可以重新看这篇文章,根据文中的 ...

  5. 一篇文章看懂makefile编写规则

    该篇文章为转载,是对原作者系列文章的总汇加上标注. 支持原创,请移步陈浩大神博客:(最原始版本) http://blog.csdn.net/haoel/article/details/2886 我转自 ...

  6. Java读取excel的方式,一篇文章看懂(详细)

    目录 一.excel读取的两种方式 1.1 jxl 和 poi 的区别和选择 二.jxl 的使用 2.1 导入相关依赖 2.2 操作 三.poi 的使用 3.1 导入相关依赖 3.2 操作 四.总结 ...

  7. 一篇文章看懂select_poll_epoll

    书上还有各种博客介绍select.poll.epoll基本上都是一上来就介绍数据结构,参数等等,这对新手很不友好啊,于是看到了知乎大神的帖子:https://www.zhihu.com/questio ...

  8. 一篇文章看懂有关iOS开发语言的一切!

    看什么看!点我呀!全栈程序员,免费入门到精通! 作者丨开发之家 https://www.jianshu.com/p/1fd231cfe54b 前言 iOS开发语言有哪些?OS开发语言主要包括什么?iO ...

  9. 阿米巴经营模式的核心是什么?一篇文章看懂!

    阿米巴,企业经营管理模式中使用这一词,称作"阿米巴经营模式".阿米巴经营就是以各个阿米巴的领导为核心,让其自行制定各自的计划,并依靠全体成员的智慧和努力来完成目标. 通过这样一种做 ...

最新文章

  1. java中两个Integer类型的值相比较的问题
  2. 解决 wget 使用 https 下载报错的问题
  3. Dubbo Admin服务测试功能
  4. numpy复习总结,为深度学习打下基础
  5. 语法练习——动名词专项训练
  6. 创建mysql对象_mysql创建statement对象
  7. 格式工厂 wav 比特率_这可能是 Mac 上最强格式转换工具
  8. 如何在电脑端免费下载歌曲?
  9. ODC20:更开放的行业解决方案,进击的OPPO IoT生态与云能力
  10. 求解:Encountered internal error running command: TypeError: Cannot read property 'toLowerCase' of unde
  11. Vue04- 样式使用
  12. 计算机中用函数排序,编写一个sort()函数,实现选择法排序,在主函数中输入10个整数,主函数调用sort()函数实现排序后将数据输出。...
  13. 软件包 jdk1.8-2000:1.8.0_281-fcs.aarch64 是针对 different 构架的
  14. 冗余系统研究:智能汽车的最后安全防线
  15. 计算机网络组成原理——基本概念
  16. BaseDao.java
  17. 记一次服务器被挖矿木马攻击的经历
  18. [转帖]Windows7/2008中批量删除隧道适配器的方法
  19. ui和平面设计的区别?ui设计做什么的?
  20. 关于Eclipse无法创建web工程问题

热门文章

  1. 一个类可以有一个接口,接口可以有一个Java类吗?
  2. MyEclipse2017破解设置与maven项目搭建
  3. PL/SQL Developer中文版下载以及使用图解(绿色版)
  4. Tensorflow C3D完成视频动作识别
  5. Kubernetes 部署 Traefik Ingress 控制器 (1.7.12)
  6. 精通ASP.NET MVC ——属性路由
  7. python property作用_python中@property的作用和getter setter的解释
  8. tcp unity 图片_Unity 简易聊天室(基于TCP)(2)
  9. java编写通信录管理系统_Java 实现通讯录管理系统教程
  10. java cassandra连接池_java操作cassandra(连接池)