在日常开发过程中,大多数是基于调用一个方法时,里面业务执行的是同步方式实现,很少用到异步的方式去实现。
同步调用:同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行;
异步调用:异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序。
对比发现异步比同步快很多,节省很多的时间,也会相对提高效率。
一、接下来我们测试同步,我们可以通过一个简单的接口调用实现如图
//测试同步
@RequestMapping(value = “/testSync”)
@ResponseBody
public String testSync()throws InterruptedException {
System.out.println("当前线程名:"+Thread.currentThread().getName());
LocalTime time1 = LocalDateTime.now().toLocalTime();
System.out.println("第一次调用时间:"+ time1);
Thread.sleep(4000);//此时需要处理一个InterruptedException异常
LocalTime time2 = LocalDateTime.now().toLocalTime();
System.out.println("第二次调用时间:"+ time2);
//得出两个时间差
Duration duration = Duration.between(time1,time2);
long timeC = duration.toMillis();
System.out.println("两者相差:"+ (timeC/1000)+"秒");
return "666666";
}
从结果来看,我们可也看到同步是在我们上一个执行完才开始执行的,中间会有个等待的时间,比如我们在记录日志时候就可以通过异步方式来实现,不需要等待。
二、我们来试试异步方式调用一个程序的结果,我们使用的是springboot,接下来我们采用两种方式来实现看看,
1.使用@Async和@EnableAsync注解;
(简单介绍一下,spring从3.0版本开始支持异步调用,在方法或者类上一个@Async注解就可以,注意这个类不能是加了@Configuration注解,否则不支持,原因@Configuration注解会对当前类进行代理增强,这个时候返回的类是一个经过代理的类,这个时候的方法可能已经不是原方法了。
在3.1版本的时候加上了@EnableAsync注解,基于@Import注解进行扩展的)
2.通过自定义多线程完成;
**1.**我们自定义多线程
如图所示
// 测试异步
@RequestMapping(value = "/testAsync")
@ResponseBody
public String testAsync()throws InterruptedException {
System.out.println("controller当前线程名:"+Thread.currentThread().getName());
LocalTime time1 = LocalDateTime.now().toLocalTime();
System.out.println("第一次调用时间:"+ time1);
Thread thread = new Thread(new TestAsync());
thread.start();
//Thread.sleep(4000);//此时需要处理一个InterruptedException异常
LocalTime time2 = LocalDateTime.now().toLocalTime();
System.out.println("第二次调用时间:"+ time2);
//得出两个时间差
Duration duration = Duration.between(time1,time2);
long timeC = duration.toMillis();
System.out.println("1号结果两者相差:"+ (timeC/1000)+"秒");
return "666666";
}
我们再写一个实现Runnable接口
把从同步方式抽出来方法或者业务放进线程执行体中。
如图
public class TestAsync implements Runnable {
@Override
public void run() {
System.out.println("Async当前线程名称:"+Thread.currentThread().getName());
LocalTime time1 = LocalDateTime.now().toLocalTime();
System.out.println("第一次调用时间:"+ time1);
try{
Thread.sleep(3000);
}catch (InterruptedException e){
e.printStackTrace();
}
LocalTime time2 = LocalDateTime.now().toLocalTime();
System.out.println("第二次调用时间:"+ time2);
//得出两个时间差
Duration duration = Duration.between(time1,time2);
long timeC = duration.toMillis();
System.out.println("2号结果两者时间相差:"+ (timeC/1000)+"秒");
}
}
然后我们启动执行后,可以看到执行结果
我们可以看到2号结果在最后打印,说明异步处理成功,调用controller中1号结果方法不需要等待2号结果那个方法执行完。
2.我们来使用@Async和@EnableAsync注解进行处理
首先我们新建一个配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
public class AsyncConfigurer extends AsyncConfigurerSupport {
@Bean
public ThreadPoolTaskExecutor asyncExecutor(){
ThreadPoolTaskExecutor threadPoolExecutor = new ThreadPoolTaskExecutor();
// 设置核心线程数
threadPoolExecutor.setCorePoolSize(3);
// 设置最大线程数
threadPoolExecutor.setMaxPoolSize(3);
// 等待所有任务结束后再关闭线程池
threadPoolExecutor.setWaitForTasksToCompleteOnShutdown(true);
threadPoolExecutor.setAwaitTerminationSeconds(60*15);
return threadPoolExecutor;
}
}
*这个类也试了一下,去掉也能执行。
我们弄个测试方法
//测试异步 注解方式
@RequestMapping(value = "/testAsyncZhuJie")
@ResponseBody
public String testAsyncZhuJie()throws InterruptedException {
System.out.println("controller中当前线程名:"+Thread.currentThread().getName());
LocalTime time1 = LocalDateTime.now().toLocalTime();
System.out.println("controller中第一次调用时间:"+ time1);
//业务调用
asyncService.testAsyncZhuJie();
Thread.sleep(4000);//此时需要处理一个InterruptedException异常
LocalTime time2 = LocalDateTime.now().toLocalTime();
System.out.println("controller中第二次调用时间:"+ time2);
//得出两个时间差
Duration duration = Duration.between(time1,time2);
long timeC = duration.toMillis();
System.out.println("controller中1号结果两者相差:"+ (timeC/1000)+"秒");
return "666666";
}
然后在service实现类中写了个方法方便调用
@Async
@Override
public void testAsyncZhuJie() {
System.out.println("Service中Async当前线程名称:"+Thread.currentThread().getName());
LocalTime time1 = LocalDateTime.now().toLocalTime();
System.out.println("Service中第一次调用时间:"+ time1);
try{
Thread.sleep(3000);
}catch (InterruptedException e){
e.printStackTrace();
}
LocalTime time2 = LocalDateTime.now().toLocalTime();
System.out.println("Service中第二次调用时间:"+ time2);
//得出两个时间差
Duration duration = Duration.between(time1,time2);
long timeC = duration.toMillis();
System.out.println("Service中2号结果两者时间相差:"+ (timeC/1000)+"秒");
}
执行结果如下
至此我们可以看到,如果service方法在最后打印,说明controller在执行后不需要等待service全部执行完才走,注意这个@Async注解没有会失效,结果就是等service全部执行完了,controller才走接下来的方法。
以上都是个人亲测,也是日常开发过程中所用的方式。如果有什么不对的地方,希望大家指正,谢谢了!