Springboot中可以使用@Async轻松实现执行异步任务。
1.@Async注解
@Async是springboot的异步注解,可以轻松实现执行异步任务。从本质讲,@Async内部就是通过多线程实现的。
Springboot实现异步常见的有以下两种方式:
2.使用@Async注解的注意事项 - 调用@Async方法时不能在同一个类中,不然会异步调用会不会生效。 - 启动类上一定要加上@EnableAsync注解 ,代表开启异步。 - 使用@Async注解的方法必须是public方法,使用private关键字,一定会失败。 - 使用@Async注解的方法返回值只能是void或者Future。 - 不要尝试在junit中使用单元测试(@Test)来测试有@Async注解的异步方法。
需要说明的就是第五点,因为单元测试本身就是一个测试线程,用来测试异步任务显然是不靠谱的,因为测试线程可能已经结束了,而异步任务甚至还未开始或者没有结束。
3.实例
项目结构图如下:

1)在启动类上开启异步注解。
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
2)编写异步任务类
AsyncTask.java
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import java.util.concurrent.Future;
@Component
public class AsyncTask {
//异步任务没有返回值的。
@Async
public void asyncMethodNoReturnValue() throws InterruptedException {
System.out.println("asyncMethodNoReturnValue thread: "+Thread.currentThread().getName());
long startTime = System.currentTimeMillis();
// 异步线程延时 5s 返回结果
Thread.sleep(5000);
System.out.println("asyncMethodNoReturnValue thread execute ending....");
long endTime = System.currentTimeMillis();
System.out.println("asyncMethodNoReturnValue 总耗时:" + (endTime - startTime)+" ms");
}
//同步任1务返回Future
public Future<Integer> syncMethod1() throws InterruptedException {
System.out.println("syncMethod1 thread: "+Thread.currentThread().getName());
// 异步线程延时 5s 返回结果
Thread.sleep(5000);
return new AsyncResult<>(100);
}
//同步任务2返回Future
public Future<Integer> syncMethod2() throws InterruptedException {
System.out.println("syncMethod2 thread: "+Thread.currentThread().getName());
// 异步线程延时 3s 返回结果
Thread.sleep(3000);
return new AsyncResult<>(200);
}
//异步任务1返回Future
@Async
public Future<Integer> asyncMethod1() throws InterruptedException {
System.out.println("asyncMethod1 thread: "+Thread.currentThread().getName());
// 异步线程延时 5s 返回结果
Thread.sleep(5000);
return new AsyncResult<>(100);
}
//异步任务2返回Future
@Async
public Future<Integer> asyncMethod2() throws InterruptedException {
System.out.println("asyncMethod2 thread: "+Thread.currentThread().getName());
// 异步线程延时 3s 返回结果
Thread.sleep(3000);
return new AsyncResult<>(200);
}
}
3)测试接口
import com.simoniu.spt.course.task.AsyncTask;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
@RestController
public class AsyncController {
@Resource
private AsyncTask asyncTask;
@GetMapping("/async")
public Map<String, Object> executeAsyncTask() {
Map<String, Object> result = new HashMap<String, Object>();
try {
long startTime = System.currentTimeMillis();
asyncTask.asyncMethodNoReturnValue();
result.put("code", 200);
result.put("msg", "success");
long endTime = System.currentTimeMillis();
System.out.println("AsyncController=>executeAsyncTask(), 总耗时:" + (endTime - startTime) + " ms");
return result;
} catch (Exception ex) {
ex.printStackTrace();
result.put("code", 400);
result.put("msg", "error");
return result;
}
}
@GetMapping("/syncfuture")
public Map<String, Object> executeSyncFutureTask() {
Map<String, Object> result = new HashMap<String, Object>();
try {
long startTime = System.currentTimeMillis();
Future<Integer> future1 = asyncTask.syncMethod1();
Future<Integer> future2 = asyncTask.syncMethod2();
result.put("code", 200);
result.put("msg", "success");
long endTime = System.currentTimeMillis();
System.out.println("AsyncController=>executeSyncFutureTask(), 总耗时:" + (endTime - startTime) + " ms");
return result;
} catch (Exception ex) {
ex.printStackTrace();
result.put("code", 400);
result.put("msg", "error");
return result;
}
}
@GetMapping("/asyncfuture")
public Map<String, Object> executeAsyncFutureTask() {
Map<String, Object> result = new HashMap<String, Object>();
try {
long startTime = System.currentTimeMillis();
Future<Integer> future1 = asyncTask.asyncMethod1();
Future<Integer> future2 = asyncTask.asyncMethod2();
StringBuilder sb = new StringBuilder();
sb.append(future1.get() + " ");
sb.append(future2.get() + " ");
result.put("code", 200);
result.put("msg", "success");
result.put("data", sb.toString());
long endTime = System.currentTimeMillis();
System.out.println("AsyncController=>executeAsyncFutureTask(), 总耗时:" + (endTime - startTime) + " ms");
return result;
} catch (Exception ex) {
ex.printStackTrace();
result.put("code", 400);
result.put("msg", "error");
return result;
}
}
}
4)测试没有返回值的异步方法。
浏览器输入:http://localhost:8080/asyncdemo/async
控制台输出:
AsyncController=>executeAsyncTask(), 总耗时:0 ms
asyncMethodNoReturnValue thread: task-2
asyncMethodNoReturnValue thread execute ending....
asyncMethodNoReturnValue 总耗时:5000 ms
Controller先结束,异步任务后结束。OK!
5)测试返回Future的同步任务。
浏览器输入:http://localhost:8080/asyncdemo/syncfuture 控制台输出:
syncMethod1 thread: http-nio-8080-exec-6
syncMethod2 thread: http-nio-8080-exec-6
AsyncController=>executeSyncFutureTask(), 总耗时:8003 ms
因为是同步任务,总耗时是两个同步任务时间之和,大概8秒。OK!
6)测试返回Future的异步任务。
浏览器输入:http://localhost:8080/asyncdemo/asyncfuture 控制台输出:
asyncMethod1 thread: task-3
asyncMethod2 thread: task-4
AsyncController=>executeAsyncFutureTask(), 总耗时:5001 ms
因为是异步任务,总耗时是最耗时的任务时间,大概5秒。OK!
7)证明@Async本质就是多线程。
我们把 /syncfuture 接口的实现改为启动两个线程实现。
@GetMapping("/syncfuture")
public Map<String, Object> executeSyncFutureTask() {
Map<String, Object> result = new HashMap<String, Object>();
try {
long startTime = System.currentTimeMillis();
/*
Future<Integer> future1 = asyncTask.syncMethod1();
Future<Integer> future2 = asyncTask.syncMethod2();*/
Thread th1 = new Thread(() -> {
try {
asyncTask.syncMethod1();
} catch (Exception ex) {
ex.printStackTrace();
}
});
Thread th2 = new Thread(() -> {
try {
asyncTask.syncMethod2();
} catch (Exception ex) {
ex.printStackTrace();
}
});
th1.start();
th2.start();
th1.join();
th2.join();
result.put("code", 200);
result.put("msg", "success");
long endTime = System.currentTimeMillis();
System.out.println("AsyncController=>executeSyncFutureTask(), 总耗时:" + (endTime - startTime) + " ms");
return result;
} catch (Exception ex) {
ex.printStackTrace();
result.put("code", 400);
result.put("msg", "error");
return result;
}
}
浏览器输入:http://localhost:8080/asyncdemo/syncfuture 控制台输出:
syncMethod2 thread: Thread-16
syncMethod1 thread: Thread-15
AsyncController=>executeSyncFutureTask(), 总耗时:5006 ms
因为是异步任务,总耗时是最耗时的任务时间,大概5秒。与使用@Async注解效果完全相同!
小结:
@Async是springboot的异步注解,可以轻松实现执行异步任务。 使用@Async要注意以下规则: - 调用@Async方法时不能在同一个类中,不然会异步调用会不会生效。 - 启动类上一定要加上@EnableAsync注解 ,代表开启异步。 - 使用@Async注解的方法必须是public方法,使用private关键字,一定会失败。 - 使用@Async注解的方法返回值只能是void或者Future。 - 不要尝试在junit中使用单元测试(@Test)来测试有@Async注解的异步方法。