springboot—@Async实现异步调用及异步回调Future「建议收藏」

springboot—@Async实现异步调用及异步回调Future「建议收藏」异步调用相对的是同步调用。同步方法调用的时候必须是按照顺序执行的,上一行代码执行完,才会执行下一行。而异步方法调用是相当于多个线程执行,不需要等待上一行代码的执行结果。首先测试方法同步的情况:controller:packagespringboot_async.async_test;importorg.springframework.beans.factory.annot…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

异步调用相对的是同步调用。

同步方法调用的时候必须是按照顺序执行的,上一行代码执行完,才会执行下一行。而异步方法调用是相当于多个线程执行,不需要等待上一行代码的执行结果。

首先测试方法同步的情况:

controller:

package springboot_async.async_test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

	@Autowired
	private MyService myService;
	
	@RequestMapping("/test")
	public String getInedx() throws InterruptedException {
		System.out.println("开始访问");
		long l1 = System.currentTimeMillis();
		myService.JobOne();
		myService.JobTwo();
		myService.JobThree();
		long l2 = System.currentTimeMillis();
		
		System.out.println("结束访问,用时"+(l2-l1));
		return "finished";
	}
}

service:

package springboot_async.async_test;

import org.springframework.stereotype.Service;

@Service
public class MyService {

	
	public void JobOne() throws InterruptedException {
		System.out.println("开始执行任务一");
		long l1 = System.currentTimeMillis();
		Thread.sleep(2000);
		long l2 = System.currentTimeMillis();
		System.out.println("任务一用时"+(l2-l1));
		
	}
	
	
	public void JobTwo() throws InterruptedException {
		System.out.println("开始执行任务二");
		long l1 = System.currentTimeMillis();
		Thread.sleep(2000);
		long l2 = System.currentTimeMillis();
		System.out.println("任务二用时"+(l2-l1));
	}
	
	public void JobThree() throws InterruptedException {
		System.out.println("开始执行任务三");
		long l1 = System.currentTimeMillis();
		Thread.sleep(2000);
		long l2 = System.currentTimeMillis();
		System.out.println("任务三用时"+(l2-l1));
	}
}

启动类:

package springboot_async;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class App {

	public static void main(String[] args) {

		SpringApplication.run(App.class, args);
	}

}

运行结果,我们以运行四次为例:

springboot---@Async实现异步调用及异步回调Future「建议收藏」

我们可以看到多次实验运行的结果都是按照调用方法的顺序进行执行。

下面我们使用springboot提供的@Async来实现异步方法调用。

首先要在启动类上面使用@EnableAsync开始异步方法调用,然后在你要调用的每一个方法上面都要添加@Async,表明异步调用该方法。相当于开启了新的线程,在调用该方法的时候不需要等待上一行代码是否执行完成。

conrtoller不需要做修改。

service:

package springboot_async.async_test;

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

@Service
public class MyService {

	
	@Async
	public void JobOne() throws InterruptedException {
		System.out.println("开始执行任务一");
		long l1 = System.currentTimeMillis();
		Thread.sleep(2000);
		long l2 = System.currentTimeMillis();
		System.out.println("任务一用时"+(l2-l1));
		
	}
	
	@Async
	public void JobTwo() throws InterruptedException {
		System.out.println("开始执行任务二");
		long l1 = System.currentTimeMillis();
		Thread.sleep(2000);
		long l2 = System.currentTimeMillis();
		System.out.println("任务二用时"+(l2-l1));
	}
	
	
	@Async
	public void JobThree() throws InterruptedException {
		System.out.println("开始执行任务三");
		long l1 = System.currentTimeMillis();
		Thread.sleep(2000);
		long l2 = System.currentTimeMillis();
		System.out.println("任务三用时"+(l2-l1));
	}
}

启动类:

package springboot_async;

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


@SpringBootApplication
@EnableAsync
public class App {

	public static void main(String[] args) {

		SpringApplication.run(App.class, args);
	}

}

运行测试一:

springboot---@Async实现异步调用及异步回调Future「建议收藏」

运行测试二:

springboot---@Async实现异步调用及异步回调Future「建议收藏」

运行测试三:

springboot---@Async实现异步调用及异步回调Future「建议收藏」

经过测试可以看到任务执行的顺序是随机的,与方法调用的顺序无关,就说明这些方法是异步调用的。

在上面的测试中我们也可以发现主调用方法controller没有等到调用方法执行完就结束了当前的任务,那么我们如果想要知道在整个任务调用的三个方法全部执行完总共的时长该怎么办呢,下面就可以用到异步回调。

异步回调就是让每个被调用的方法返回一个Future类型的值,而Spring提供了一个Future接口的子类:AsyncResult,所以我们可以返回的时候new一个AsyncResult类型的值。

Future接口及实现类:

springboot---@Async实现异步调用及异步回调Future「建议收藏」

Futrue的方法:

springboot---@Async实现异步调用及异步回调Future「建议收藏」

isDone()返回Boolean类型值,用来判断该异步任务是否执行完成,如果执行完成,则返回true,如果未执行完成,则返回false.

cancel(boolean mayInterruptRunning)返回boolean类型值,参数也是一个boolean类型的值,用来传入是否可以打断当前正在执行的任务。如果参数是true且当前任务没有执行完成 ,说明可以打断当前任务,那么就会返回true,如果当前任务还没有执行,那么不管参数是true还是false,返回值都是true,如果当前任务已经完成,那么不管参数是true还是false,那么返回值都是false,如果当前任务没有完成且参数是false,那么返回值也是false。总结下来就是:1.如果任务还没执行,那么如果想取消任务,就一定返回true,与参数无关。2.如果任务已经执行完成,那么任务一定是不能取消的,所以此时返回值都是false,与参数无关。3.如果任务正在执行中,那么此时是否取消任务就看参数是否允许打断(true/false)。

isCancelled()返回的是boolean类型,如果是上面总结的第三种情况,这才是真正意义上有机会被取消的任务,那么此时如果上面的方法返回的是true,那么说明任务取消成功了,则这个方法返回的也就是true。

get()返回的是在异步方法中最后return 的那个对象中的value的值。

get(long timeout,TimeUnit unit)这个方法和get()的功能是一样的(在方法执行没有超时的情况下效果是一样的),只不过这里参数中设置了超时时间,因为get()在执行的时候是需要等待回调结果的,是阻塞在那里的,如果不设置超时时间,它就阻塞在那里直到有了任务执行完成。我们设置超时时间,就可以在当前任务执行太久的情况下中断当前任务,释放线程,这样就不会导致一直占用资源。参数一是时间的数值,参数二是参数一的单位,可以在TimeUnit这个枚举中选择单位。如果任务执行超时,则抛出TimeOut异常,返回的message就是null。

TimeOut枚举的值:

springboot---@Async实现异步调用及异步回调Future「建议收藏」

使用异步回调:

在controller中无限循环判断异步方法是否执行完成。

在service的方法中返回Future值。

controller:

package springboot_async.async_test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

	@Autowired
	private MyService myService;
	
	@RequestMapping("/test")
	public String getInedx() throws InterruptedException, ExecutionException {
		System.out.println("开始访问");
		long l1 = System.currentTimeMillis();
		Future<String> r1 = myService.JobOne();
		Future<String> r2 = myService.JobTwo();
		Future<String> r3 = myService.JobThree();
		while(true) {//死循环,每隔2000ms执行一次,判断一下这三个异步调用的方法是否全都执行完了。
			if(r1.isDone() && r2.isDone() && r3.isDone()) {//使用Future的isDone()方法返回该方法是否执行完成
				//如果异步方法全部执行完,跳出循环
				break;
			}
			Thread.sleep(2000);//每隔2000毫秒判断一次
		}
		long l2 = System.currentTimeMillis();//跳出while循环时说明此时三个异步调用的方法都执行完成了,此时得到当前时间
		
		String result = r1.get();
		System.out.println("结束访问,用时"+(l2-l1));
		System.out.println("使用get方法获得的返回内容:"+result);
		return "finished";
	}
}

service :

package springboot_async.async_test;

import java.util.concurrent.Future;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

@Service
public class MyService {

	
	@Async
	public Future<String> JobOne() throws InterruptedException {
		System.out.println("开始执行任务一");
		long l1 = System.currentTimeMillis();
		Thread.sleep(2000);
		long l2 = System.currentTimeMillis();
		System.out.println("任务一用时"+(l2-l1));
		return new AsyncResult<String>("任务一完成");//可以使用try,catch定义在正常完成时返回一个success信息,出现异常时返回error信息。
	}
	
	@Async
	public Future<String> JobTwo() throws InterruptedException {
		System.out.println("开始执行任务二");
		long l1 = System.currentTimeMillis();
		Thread.sleep(2000);
		long l2 = System.currentTimeMillis();
		System.out.println("任务二用时"+(l2-l1));
		return new AsyncResult<String>("任务二完成");
	}
	
	
	@Async
	public Future<String> JobThree() throws InterruptedException {
		System.out.println("开始执行任务三");
		long l1 = System.currentTimeMillis();
		Thread.sleep(2000);
		long l2 = System.currentTimeMillis();
		System.out.println("任务三用时"+(l2-l1));
		return new AsyncResult<String>("任务三完成");
	}
}

执行结果:

springboot---@Async实现异步调用及异步回调Future「建议收藏」

此时我们就得到了所有异步任务全部执行完成的时间,全部执行完成后才让整个controller执行结束。而且我们也发现在controller中调用Future的get()时,得到的就是JobOne()方法中return new AsyncResult中的值。

下面我们看一下使用有参的get()方法的超时效果,这里以JobTwo来测试,特别要注意get(timeout,unit)放的位置是在while循环之前,也就是任务还没有执行完成之前:

controller:

@RestController
public class TestController {

	@Autowired
	private MyService myService;
	
	@RequestMapping("/test")
	public String getInedx() throws InterruptedException, ExecutionException, TimeoutException {
		System.out.println("开始访问");
		long l1 = System.currentTimeMillis();
		Future<String> r1 = myService.JobOne();
		Future<String> r2 = myService.JobTwo();
		Future<String> r3 = myService.JobThree();
		String result2 = r2.get(50, TimeUnit.MILLISECONDS);
		System.out.println("使用有参get()得到的返回值"+result2);
		while(true) {//死循环,每隔2000ms执行一次,判断一下这三个异步调用的方法是否全都执行完了。
			if(r1.isDone() && r2.isDone() && r3.isDone()) {//使用Future的isDone()方法返回该方法是否执行完成
				//如果异步方法全部执行完,跳出循环
				break;
			}
			Thread.sleep(2000);//每隔2000毫秒判断一次
		}
		long l2 = System.currentTimeMillis();//跳出while循环时说明此时三个异步调用的方法都执行完成了,此时得到当前时间
		
		String result = r1.get();
		/*
		 * String result2 = r2.get(50, TimeUnit.MILLISECONDS);
		   System.out.println("使用有参get()得到的返回值"+result2);
		   最开始在实验的时候把这两句话放在了这里,就一直测试不出来超时异常,
		   后来才发现把超时设置在while循环判断已经任务完成之后,那么超时设置当然就不起作用了,
		   所以放在这里也就不会出现超时异常,应该放在while循环之前即在任务开始执行之后就对其执行时长进行超时设置才会对时间真正起到限制作用。
		 */
		System.out.println("结束访问,用时"+(l2-l1));
		System.out.println("使用get方法获得的返回内容:"+result);
		
		return "finished";
	}
}

service中只是改变了一下线程的睡眠时间,让其超过我们设置的时间:

springboot---@Async实现异步调用及异步回调Future「建议收藏」

此时执行的结果就是出现TimeOut异常,且异常Message是null。

springboot---@Async实现异步调用及异步回调Future「建议收藏」

 springboot---@Async实现异步调用及异步回调Future「建议收藏」

那么为什么在任务二超时了之后仍然会打印输出任务二方法中的那句用时呢?我的理解是抛出异常的只是get()方法,而任务二线程本身并不受get方法异常的影响,因为get()是在controller这个方法中的另一个线程,所以任务二会正常执行它的任务,只是get()在检测时自己本身设置超时的行为让它出现了异常。从而,在controller这个线程中出现了异常之后,那么get()之后的语句就不再执行,所以后面的三个方法异步调用总时长的这些语句都没有被执行也就没有打印输出。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/157831.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • anaconda卸载方法

    anaconda卸载方法卸载anaconda

    2022年6月15日
    83
  • MySQL字符串分割_c语言中如何截取字符串

    MySQL字符串分割_c语言中如何截取字符串有分隔符的字符串拆分题目要求数据库中num字段值为:实现的效果:需要将一行数据变成多行实现的SQLSELECTSUBSTRING_INDEX(SUBSTRING_INDEX(‘7654,7698,7782,7788′,’,’,help_topic_id+1),’,’,-1)ASnumFROMmysql.help_topic…

    2022年10月7日
    5
  • 调用wsdl接口 使用xml格式为参数

    调用wsdl接口 使用xml格式为参数

    2020年11月9日
    302
  • MPU9250传感器

    MPU9250内部包括3轴陀螺仪、3轴加速度计和3轴磁力计,这3个功能输出都是16位的数字量;可以通过常用的数据总线(IIC)接口和单片机进行数据交互,传输速率400kHz/s。陀螺仪的角速度测量范围±2000(°/s),具有良好的动态响应特性。加速度计的测量范围最大为±16g(g为重力加速度),静态测量精度高。磁力计采用高灵度霍尔型传感器进行数据采集,磁感应强度测量范围为±4800μT,可用于对偏航角的辅助测量。MPU9250自带的数字运动处理器DMP硬件加速引擎,可

    2022年4月8日
    197
  • 一个全心全意帮助程序猿跳槽的创业者「建议收藏」

    一个全心全意帮助程序猿跳槽的创业者

    2022年1月26日
    40
  • 女生学java 怎么样_女生学java怎么样?好就业吗?

    女生学java 怎么样_女生学java怎么样?好就业吗?现在女生学什么好?女生学什么专业好就业?女生学Java怎么样?不少男性开发者认为“女性水平比较低,也就做做测试”,这是男性领导者经常说的一句话,虽然此话含有其个人经验和行业经验,但我们并不完全认同。因为女性比男性有着独特的优势,就这个行业而言:一是女性在细心和耐力方面比男性表现得更为突出;二是在技术管理方面女性更有优势。现在的研发项目中很少是个人独立去做一件事情。多数是需要团队合作的。通常那些技术…

    2022年7月9日
    17

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号