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地址:链接地址

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

相关文章

  • Spring Boot中@RequestParam参数的5种情况说明

    Spring Boot中@RequestParam参数的5种情况说明

    这篇文章主要介绍了Spring Boot中@RequestParam参数的5种情况说明,具有很好的参考价值,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • java处理异常Exception的方法总结

    java处理异常Exception的方法总结

    在Java中处理异常并不是一个简单的事情,不仅仅初学者很难理解,即使一些有经验的开发者也需要花费很多时间,本文为大家整理了java处理异常Exception的一些常用方法,希望对大家有所帮助
    2023-09-09
  • Java long 转成 String的实现

    Java long 转成 String的实现

    这篇文章主要介绍了Java long 转成 String的实现,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • Java工具jsch.jar实现上传下载

    Java工具jsch.jar实现上传下载

    这篇文章主要为大家详细介绍了Java操作ftp的一款工具,利用jsch.jar针对sftp的上传下载工具类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • IDEA使用SpringAssistant插件创建SpringCloud项目

    IDEA使用SpringAssistant插件创建SpringCloud项目

    IDEA 功能强大,可以用来高效的开发应该程序。它还支持第三方插件、用户可以根据需要添加自己喜欢的插件。下面介绍如何使用 IDEA 创建 Spring Cloud 项目
    2021-06-06
  • Java中JSONObject和Map<String, Object>的转换方法

    Java中JSONObject和Map<String, Object>的转换方法

    平时对接口时,经常遇到json字符串和map对象之间的交互,这篇文章主要给大家介绍了关于Java中JSONObject和Map<String, Object>的转换方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-07-07
  • 一文看懂springboot实现短信服务功能

    一文看懂springboot实现短信服务功能

    项目中的短信服务基本上上都会用到,简单的注册验证码,消息通知等等都会用到。这篇文章主要介绍了springboot 实现短信服务功能,需要的朋友可以参考下
    2019-10-10
  • java代码块之简易qq登录界面及按钮颜色设置代码

    java代码块之简易qq登录界面及按钮颜色设置代码

    这篇文章主要介绍了java代码块之简易qq登录界面及按钮颜色设置代码,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • 解决java项目jar打包后读取文件失败的问题

    解决java项目jar打包后读取文件失败的问题

    这篇文章主要介绍了解决java项目jar打包后读取文件失败的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • 创建java多线程程序

    创建java多线程程序

    Java 给多线程编程提供了内置的支持。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。希望本篇文章能够给你带来帮助
    2021-06-06

最新评论