SpringBoot实现定时任务和异步调用

 更新时间:2019年04月26日 11:17:09   作者:叶落自飘零  
这篇文章主要为大家详细介绍了SpringBoot实现定时任务和异步调用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了SpringBoot实现定时任务和异步调用的具体代码,供大家参考,具体内容如下

环境:

jdk1.8;spring boot2.0.2;Maven3.3

摘要说明:

定时任务:定时任务是业务场景中经常出现的一种情况如:定时发送邮件,短信、定时统计监控数据、定时对账等

异步调用:一个都买流程可能包括下单、发货通知、短信推送、消息推送等,其实除了下单这个主要程序是主程序,其他子程序可以同时进行且不影响主程序的运行,这个时候就可以使用异步调用来调用这些子程序;

步骤:

1.定时任务

a.在spring boot主类上使用注解@EnableScheduling启动定时任务:

package com.example.demo;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
 
//启动定时任务
@EnableScheduling
@SpringBootApplication
public class DemoApplication {
 
 public static void main(String[] args) {
 SpringApplication.run(DemoApplication.class, args);
 }
}

b.实现定时任务(使用@Component注解来标注组件)

 /**
 * @模块名:demo
 * @包名:com.example.demo.test1.component
 * @描述:SchedulingComponent.java
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年9月29日上午10:19:37
 */
 
package com.example.demo.test1.component;
 
import java.util.Date;
 
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
/**
 * @模块名:demo
 * @包名:com.example.demo.test1.component @类名称: SchedulingComponent
 * @类描述:【类描述】用于测试定时任务 @版本:1.0
 * @创建人:cc
 * @创建时间:2018年9月29日上午10:19:37
 */
@Component
public class SchedulingComponent {
 
 /**
 * 
 * @方法名:testScheduling1
 * @方法描述【方法功能描述】测试定时任务,没三秒执行一次
 * @修改描述【修改描述】
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年9月29日 上午10:26:20
 * @修改人:cc
 * @修改时间:2018年9月29日 上午10:26:20
 */
 @Scheduled(fixedRate = 3000)
 public void testScheduling1() {
 
 System.out.println("执行时间为"+new Date()+"执行testScheduling1");
 }
}
@Scheduled注解和之前spring使用xml配置定时任务类似:

@Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行
@Scheduled(fixedDelay = 5000) :上一次执行完毕时间点之后5秒再执行
@Scheduled(initialDelay=1000, fixedRate=5000) :第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次
@Scheduled(cron="*/5 * * * * *") :通过cron表达式定义规则

c.上述方法写好后启动服务看下控制台结果:

2.异步调用

a.首先在spring boot主类上使用注解@EnableAsync启动异步调用

package com.example.demo;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
 
//启动异步调用
@EnableAsync
@SpringBootApplication
public class DemoApplication {
 
 public static void main(String[] args) {
 SpringApplication.run(DemoApplication.class, args);
 }
}

b.sping boot异步调用很简单,只需使用@Async注解标明方法(接口方法)异步

package com.example.demo.test1.component;
 
public interface TaskComponent {
 void test1() throws Exception;
 
 void test2() throws Exception;
 
 void test3() throws Exception;
}
package com.example.demo.test1.component.impl;
 
import java.util.Random;
 
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
 
import com.example.demo.test1.component.TaskComponent;
 
@Component
public class TaskComponentImpl implements TaskComponent {
 public static Random random = new Random();
 
 @Override
 @Async
 public void test1() throws InterruptedException {
 System.out.println("开始做任务一");
 long start = System.currentTimeMillis();
 Thread.sleep(random.nextInt(10000));
 long end = System.currentTimeMillis();
 System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
 
 }
 
 @Override
 @Async
 public void test2() throws InterruptedException {
 System.out.println("开始做任务二");
 long start = System.currentTimeMillis();
 Thread.sleep(random.nextInt(10000));
 long end = System.currentTimeMillis();
 System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
 
 }
 
 @Override
 @Async
 public void test3() throws InterruptedException {
 System.out.println("开始做任务三");
 long start = System.currentTimeMillis();
 Thread.sleep(random.nextInt(10000));
 long end = System.currentTimeMillis();
 System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
 
 }
 
}

c.使用测试类进行测试:

package com.example.demo;
 
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
 
import com.example.demo.test1.component.TaskComponent;
 
@RunWith(SpringRunner.class)
// 引入SpringBootTest并生成随机接口
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class AsyncTest {
 
 // 注入随机接口
 @LocalServerPort
 private int port;
 
 @Autowired
 private TaskComponent taskComponent;
 
 @Test
 public void testTask() {
 try {
 taskComponent.test1();
 taskComponent.test2();
 taskComponent.test3();
 System.out.println("执行主线程");
 // 主线程休眠10秒等待上述异步方法执行
 Thread.sleep(10000);
 }
 catch (Exception e) {
 System.out.println(e);
 }
 
 }
}

执行结果如下;可以看出三个异步方法互不影响,且不影响主线程的运行

执行主线程
开始做任务一
开始做任务二
开始做任务三
完成任务一,耗时:1401毫秒
完成任务二,耗时:4284毫秒
完成任务三,耗时:5068毫秒

d.对于这些异步执行的调用往往会给我们带来思考是不是异步调用越多越好,答案当然是否;所以在这里引入线程池来进行异步调用控制:

在spring boot主类上标注线程池:

package com.example.demo;
 
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
//启动定时任务
 
@EnableScheduling
@SpringBootApplication
public class DemoApplication {
 
 public static void main(String[] args) {
 SpringApplication.run(DemoApplication.class, args);
 }
 
 // 启动异步调用
 @EnableAsync
 @Configuration
 class TaskPoolConfig {
 // 核心线程数(setCorePoolSize)10:线程池创建时候初始化的线程数
 // 最大线程数(setMaxPoolSize)20:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
 // 缓冲队列(setQueueCapacity)200:用来缓冲执行任务的队列
 // 允许线程的空闲时间(setKeepAliveSeconds)60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
 // 线程池名的前缀(setThreadNamePrefix):设置好了之后可以方便我们定位处理任务所在的线程池
 // 线程池对拒绝任务的处理策略(setRejectedExecutionHandler):这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute
 // 方法的调用线程中运行被拒绝的任务(setWaitForTasksToCompleteOnShutdown);如果执行程序已关闭,则会丢弃该任务
 // setWaitForTasksToCompleteOnShutdown(true)该方法就是这里的关键,用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于Redis线程池的销毁。
 // 同时,这里还设置了setAwaitTerminationSeconds(60),该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
 @Bean("taskExecutor")
 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());
 executor.setWaitForTasksToCompleteOnShutdown(true);
 executor.setAwaitTerminationSeconds(60);
 return executor;
 }
 }
}

在方法实现类上使用@Async的同时标注线程池:

package com.example.demo.test1.component.impl;
 
import java.util.Random;
 
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
 
import com.example.demo.test1.component.TaskComponent;
 
@Component
public class TaskComponentImpl implements TaskComponent {
 public static Random random = new Random();
 
 @Override
 @Async("taskExecutor")
 public void test1() throws InterruptedException {
 System.out.println("开始做任务一");
 long start = System.currentTimeMillis();
 Thread.sleep(random.nextInt(10000));
 long end = System.currentTimeMillis();
 System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
 
 }
 
 @Override
 @Async("taskExecutor")
 public void test2() throws InterruptedException {
 System.out.println("开始做任务二");
 long start = System.currentTimeMillis();
 Thread.sleep(random.nextInt(10000));
 long end = System.currentTimeMillis();
 System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
 
 }
 
 @Override
 @Async("taskExecutor")
 public void test3() throws InterruptedException {
 System.out.println("开始做任务三");
 long start = System.currentTimeMillis();
 Thread.sleep(random.nextInt(10000));
 long end = System.currentTimeMillis();
 System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
 
 }
 
}

再次调用测试来发现结果没什么区别:

执行主线程
开始做任务一
开始做任务二
开始做任务三
完成任务一,耗时:1117毫秒
完成任务二,耗时:3964毫秒
完成任务三,耗时:8886毫秒

接着我们修改线程池线程数为2:

executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);

再次启动测试类可以看到,同时执行的线程数为2,只有等待前一个线程结束才能执行一个新的线程;

执行主线程
开始做任务一
开始做任务二
完成任务二,耗时:620毫秒
开始做任务三
完成任务一,耗时:2930毫秒
完成任务三,耗时:4506毫秒

3.demo地址:链接地址

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 如何使用Java爬虫批量爬取图片

    如何使用Java爬虫批量爬取图片

    这篇文章主要介绍了如何使用Java爬虫批量爬取图片,对于爬虫的入门来说,图片相对来说是比较容易获取的,因为大部分图片都不是敏感数据,所以不会遇到什么反爬措施,对于入门爬虫来说是比较合适的,需要的朋友可以参考下
    2023-04-04
  • 详解SpringMVC解决跨域的两种方案

    详解SpringMVC解决跨域的两种方案

    本篇文章主要介绍了详解SpringMVC解决跨域的两种方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • 基于Properties类操作.properties配置文件方法总结

    基于Properties类操作.properties配置文件方法总结

    这篇文章主要介绍了Properties类操作.properties配置文件方法总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 6种常见的SpringBoot拦截器使用场景及实现方式

    6种常见的SpringBoot拦截器使用场景及实现方式

    这篇文章主要为大家详细介绍了SpringBoot中6种常见的拦截器使用场景及其实现方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-04-04
  • 详解Spring基于xml的两种依赖注入方式

    详解Spring基于xml的两种依赖注入方式

    这篇文章主要介绍了详解Spring基于xml的两种依赖注入方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • SpringBoot+MyBatisPlus对Map中Date格式转换处理的方法详解

    SpringBoot+MyBatisPlus对Map中Date格式转换处理的方法详解

    在 SpringBoot 项目中, 如何统一 JSON 格式化中的日期格式。本文将为大家介绍一种方法:利用MyBatisPlus实现对Map中Date格式转换处理,需要的可以参考一下
    2022-10-10
  • springboot基于Mybatis mysql实现读写分离

    springboot基于Mybatis mysql实现读写分离

    这篇文章主要介绍了springboot基于Mybatis mysql实现读写分离,需要的朋友可以参考下
    2019-06-06
  • Springboot插件开发实战分享

    Springboot插件开发实战分享

    这篇文章主要介绍了Springboot插件开发实战分享,文章通过新建aop切面执行类MonitorLogInterceptor展开详细的相关内容,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-05-05
  • Java如何分析算法的时间和空间复杂度

    Java如何分析算法的时间和空间复杂度

    这篇文章主要介绍了Java如何分析算法的时间和空间复杂度,在计算机科学中,计算复杂性解释了算法的性能。文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-06-06
  • Java报错java.awt.AWTException: AWT的解决方法

    Java报错java.awt.AWTException: AWT的解决方法

    在Java图形用户界面(GUI)编程中,java.awt.AWTException是一个常见的异常,它通常与AWT(Abstract Window Toolkit)组件相关,这个异常可能在尝试进行与窗口、图形环境或系统剪贴板等操作时抛出,本文将详细探讨AWTException的成因,并提供多种解决方案
    2024-12-12

最新评论