SpringBoot2.X学习之整合定时任务和异步任务处理

目前市场上有很多定时任务框架,比如java自带的java.util.Timer类, 不过它配置比较麻烦,存在时间延后问题,所以不推荐,还有就是Quartz框架,它的配置更简单可以使用xml或者注解方式进行配置,如果是SpringMvc框架我们推荐使用这种。在springboot中使用定时任务直接使用它自带就行,下面我们就进行springboot定时任务以及异步任务调用的开发:

一.定时任务

1.启动类里面 @EnableScheduling开启定时任务,自动扫描

加上@EnableScheduling就能扫描根目录下所有的定时任务

package net.xdclass.base_project;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication //一个注解顶下面3个
@EnableScheduling//开启定时任务,自动扫描
public class ScheduleApplication {

	public static void main(String[] args) {
		SpringApplication.run(ScheduleApplication.class, args);
	}
}

2.写一个定时任务类

在定时任务类上加上@Component注解,纳入容器管理,刚刚开启了定时任务扫描,那么启动之后就会扫描加了@Scheduled注解的定时任务,定期执行这个方法,写一个两秒执行一次打印的定时任务

package net.xdclass.base_project.task;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @author sqz
 * @Description: 定时任务业务类
 * @date 2019/3/23 13:35
 */
@Component
public class TestTask {
    @Scheduled(fixedRate = 2000)
    public void sum(){
        System.out.println("当前时间"+new Date());
    }
}

3.测试一下

4.@Scheduled详解

在上面的入门例子中,使用了@Scheduled(fixedRate = 2000) 注解来定义每过2秒执行的任务,对于@Scheduled的使用可以总结如下几种方式: 

  • @Scheduled(fixedRate = 2000) :上一次开始执行时间点之后2秒再执行
  • @Scheduled(fixedDelay = 2000) :上一次执行完毕时间点之后2秒再执行
  • @Scheduled(initialDelay=2000, fixedRate=5000) :第一次延迟2秒后执行,之后按fixedRate的规则每5秒执行一次
  • @Scheduled(cron="*/5 * * * * *") :通过cron表达式定义规则
  • @Scheduled(fixedDelayString = "2000"):字符串形式,可以通过配置文件指定(与fixedDelay 没区别)
  • @Scheduled(fixedRateString = "2000"):  字符串形式,可以通过配置文件指定(与fixedRate 没区别)

1、fixedRate是按照一定的速率执行,是从上一次方法执行开始的时间算起,如果上一次方法阻塞住了,下一次也是不会执行,但是在阻塞这段时间内累计应该执行的次数,当不再阻塞时,一下子把这些全部执行掉,而后再按照固定速率继续执行。举例说明:

如图所示,我定义fixedRate5秒执行一次,程序中睡4秒,这时候开始执行到下次执行就是间隔5秒,因为程序没有阻塞。

这时候我定义fixedRate2秒执行一次,但是程序4秒才执行完,也就是阻塞住了,要等到程序执行完才立即开始下一次的任务执行

2、fixedDelay控制方法执行的间隔时间,是以上一次方法执行完开始算起,如上一次方法执行阻塞住了,那么直到上一次执行完,并间隔给定的时间后,执行下一次,举例说明:

 

这时候我定义的 fixedDelay2秒执行,但是程序执行时间是4秒,加上程序结束完再加上2秒才开始下次执行,所以间隔时间是6秒

cron表达式在线工具:https://tool.lu/crontab/

二.异步任务

1.异步任务执行场景

使用场景:适用于处理log、发送邮件、短信……等,比如有一个下单接口,我们需要查库存  150ms,余额校验 100ms,风控用户 100ms这三个操作,这些每个操作都需要调很多接口,这样累加起来时间就很长了,如果不是异步的话那么用时是累加的那么用户体验会非常差。如果使用了异步任务,那么就这三个操作会同时执行,是开了三个线程,不影响主线程的执行,主线程执行完会直接返回结果。如果异步任务有返回结果,那么就会取耗时最多的那个结果返回

2.springboot整合异步任务

启动类加上@EnableAsync开启异步任务

package net.xdclass.base_project;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication //一个注解顶下面3个
@EnableScheduling//开启定时任务,自动扫描
@EnableAsync//开启异步任务
public class ScheduleApplication {

	public static void main(String[] args) {
		SpringApplication.run(ScheduleApplication.class, args);
	}
}

3.编写三个异步没有返回值的任务
    这里我定义了三个异步方法,执行时间分别为1s,3s,2s ,需要在方法上加上@Async注解提供扫描,表示该方法是异步执行的方法。也可以在整个类上加上@Async标记这整个类的方法都是异步执行的方法 。这里三个方法是没有返回值的 

package net.xdclass.base_project.task;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;

/**
 * @author sqz
 * @Description: 异步任务业务类
 * @date 2019/3/23 14:34
 */
@Component
public class AsyncTask {
    @Async
    public void task1() throws InterruptedException {
        Instant begin = Instant.now();
        Thread.sleep(1000);
        Instant end = Instant.now();
        Duration duration = Duration.between(begin,end);
        System.out.println("任务1耗时:"+duration.toMillis());
    }
    @Async
    public void task2() throws InterruptedException {
        Instant begin = Instant.now();
        Thread.sleep(3000);
        Instant end = Instant.now();
        Duration duration = Duration.between(begin,end);
        System.out.println("任务2耗时:"+duration.toMillis());
    }
    @Async
    public void task3() throws InterruptedException {
        Instant begin = Instant.now();
        Thread.sleep(2000);
        Instant end = Instant.now();
        Duration duration = Duration.between(begin,end);
        System.out.println("任务3耗时:"+duration.toMillis());
    }
}

4.测试controller

这里直接注入任务类,因为他纳入了spring的管理,调用三个异步任务

package net.xdclass.base_project.controller;


import net.xdclass.base_project.domain.JsonData;
import net.xdclass.base_project.task.AsyncTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1")
public class UserController {

	@Autowired
	private AsyncTask asyncTask;

	@GetMapping("async_task")
    public JsonData testTask() throws InterruptedException {
		long begin = System.currentTimeMillis();
		asyncTask.task1();
		asyncTask.task2();
		asyncTask.task3();
		long end   = System.currentTimeMillis();
		long total = end -begin;
		System.out.println("总耗时:"+total);
    	return JsonData.buildSuccess(total);
	}
}

启动测试:

我们发现主线程耗时2ms就执行完了,如果异步任务没有返回值,主线程没有等异步任务执行完就直接返回了,这样用户体验就非常好了。 

5.编写三个异步有返回值的任务

有返回值的任务返回结果是框架提供的Future<>,是java.util.concurrent.Future并发包下的

 //获取异步结果

    public Future<String> task4() throws InterruptedException {
        Instant begin = Instant.now();
        Thread.sleep(3000);
        Instant end = Instant.now();
        Duration duration = Duration.between(begin,end);
        System.out.println("任务4耗时:"+duration.toMillis());
        return new AsyncResult<>("任务4");

    }
    public Future<String> task5() throws InterruptedException {
        Instant begin = Instant.now();
        Thread.sleep(1000);
        Instant end = Instant.now();
        Duration duration = Duration.between(begin,end);
        System.out.println("任务5耗时:"+duration.toMillis());
        return new AsyncResult<>("任务5");
    }
    public Future<String> task6() throws InterruptedException {
        Instant begin = Instant.now();
        Thread.sleep(2000);
        Instant end = Instant.now();
        Duration duration = Duration.between(begin,end);
        System.out.println("任务6耗时:"+duration.toMillis());
        return new AsyncResult<>("任务6");
    }

6.测试controller

调用三个有返回结果的任务,写一个死循环,三个任务都执行完就跳出,isDone()是Future自带的方法,判断该异步任务是否执行完成

package net.xdclass.base_project.controller;


import net.xdclass.base_project.domain.JsonData;
import net.xdclass.base_project.task.AsyncTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.Future;

@RestController
@RequestMapping("/api/v1")
public class UserController {

	@Autowired
	private AsyncTask asyncTask;

	@GetMapping("async_task")
    public JsonData testTask() throws InterruptedException {
		long begin = System.currentTimeMillis();
//		asyncTask.task1();
//		asyncTask.task2();
//		asyncTask.task3();

		Future<String> task4 = asyncTask.task4();
		Future<String> task5 = asyncTask.task5();
		Future<String> task6 = asyncTask.task6();
		for (;;){
			if (task4.isDone()&&task5.isDone()&&task6.isDone()){
				break;
			}
		}

		long end   = System.currentTimeMillis();
		long total = end -begin;
		System.out.println("总耗时:"+total);
    	return JsonData.buildSuccess(total);
	}
}

启动运行,发现耗时一共3006毫秒,他要等最迟的一个任务结束返回,如果是不需要返回结果的话几毫秒就结束了。 

注意点:
                1)要把异步任务封装到类里面,不能直接写到Controller
                2)增加Future<String> 返回结果 AsyncResult<String>("task执行完成");  
                3)如果需要拿到结果 需要判断全部的 task.isDone()
                4)通过注入方式,注入到controller里面,如果测试前后区别则改为同步则把Async注释掉 

源码地址:https://gitee.com/xuxinsunqizheng/springboot_module.git

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,可以使用@Scheduled注解来实现定时任务,可以结合@Async注解来实现多线程异步。 首先,需要在启动类上添加@EnableAsync注解,开启异步支持。然后在要执行异步任务的方法上添加@Async注解。 接下来,可以使用Java中的Executor框架来创建线程池,用于执行异步任务。可以在应用程序中创建一个线程池,并使用@Async注解将任务提交给线程池执行。 下面是一个示例代码: ```java @Configuration @EnableAsync public class AsyncConfig { @Bean(name = "taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setQueueCapacity(30); executor.setThreadNamePrefix("MyAsyncThread-"); executor.initialize(); return executor; } } @Service public class MyService { @Async("taskExecutor") @Scheduled(cron = "0 0 12 * * ?") //每天中午12点执行 public void myAsyncTask() { //异步任务内容 } } ``` 在上面的示例中,我们创建了一个名为“taskExecutor”的线程池,并将其注入到MyService中的myAsyncTask方法中。该方法使用@Async注解来指示它应该在异步线程中执行。@Scheduled注解指定了任务执行的时间。 需要注意的是,@Async注解只有在调用该方法的类通过Spring容器进行管理时才会生效。如果通过new关键字手动创建对象,@Async注解将不起作用。 希望这可以帮助你完成Spring Boot定时任务整合多线程异步的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值