异步调用几乎是处理高并发Web应用性能问题的万金油,那么什么是“异步调用”?异步调用对应的是同步调用
定义一个同步任务
@Component
public class AsyncTask {
public static Random random = new Random();
@Async
public void doTaskOne() throws InterruptedException {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时: " + (end - start) + "毫秒");
}
@Async
public void doTaskTwo() throws InterruptedException {
System.out.println("开始做认为二");
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
}
@Async
public void doTaskThree() throws InterruptedException {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
}
}
然后编写单元测试
@Test
public void testSync() throws Exception {
syncTask.doTaskOne();
syncTask.doTaskTwo();
syncTask.doTaskThree();
}
执行单元测试,结果如下:
开始做任务一
完成任务一,耗时: 6003毫秒
开始做认为二
完成任务二,耗时:1003毫秒
开始做任务三
完成任务三,耗时:4009毫秒
上述的同步调用虽然顺利的执行完成了三个任务,但是可以看到执行时间比较长,若这三个任务本身之间不存在依赖关系,可以并发执行的话,同步调用在执行效率方面就比较差,可以考虑通过异步调用的方式来并发执行。
在SpringBoot中,我们只需要通过使用@Async注解就能简单的将原来的同步函数变为异步函数。此外,还需要在SpringBoot的主程序中配置@EnableAsync注解
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
@Component
public class AsyncTask {
public static Random random = new Random();
@Async
public void doTaskOne() throws InterruptedException {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时: " + (end - start) + "毫秒");
}
@Async
public void doTaskTwo() throws InterruptedException {
System.out.println("开始做认为二");
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
}
@Async
public void doTaskThree() throws InterruptedException {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
}
}
此时,你可以反复执行单元测试,你可能会遇到各种不同的结果,比如
注: @Async所修饰的函数不要定义为static类型,这样异步调用不会生效
为了让doTaskOne、doTaskTwo、doTaskThree能正常结束,假设我们需要统计一下三个任务并发执行共耗时多少,这就需要等到上述三个函数都完成调用之后记录时间,并计算结果。
异步任务函数改造
@Component
public class FutureAsyncTask {
public static Random random = new Random();
@Async
public Future<String> doTaskOne() throws InterruptedException {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时: " + (end - start) + "毫秒");
return new AsyncResult<>("任务一完成");
}
@Async
public Future<String> doTaskTwo() throws InterruptedException {
System.out.println("开始做认为二");
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务二完成");
}
@Async
public Future<String> doTaskThree() throws InterruptedException {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("任务三完成");
}
}
编写测试用例
@Test
public void testFutureAsync() throws InterruptedException {
long start = System.currentTimeMillis();
Future<String> task1 = futureAsyncTask.doTaskOne();
Future<String> task2 = futureAsyncTask.doTaskTwo();
Future<String> task3 = futureAsyncTask.doTaskThree();
while (Boolean.TRUE) {
if (task1.isDone() && task2.isDone() && task3.isDone()) {
// 三个任务都调用完成,退出循环等待
break;
}
TimeUnit.SECONDS.sleep(1);
}
long end = System.currentTimeMillis();
System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");
}
看看我们做了哪些改变
开始做认为二
开始做任务三
开始做任务一
完成任务一,耗时: 3013毫秒
完成任务三,耗时:5002毫秒
完成任务二,耗时:9013毫秒
任务全部完成,总耗时:9075毫秒
现在我们在异步函数上添加static,使其成为静态函数,IDEA会警告

用“@Async”注释的方法必须是可重写的