简单使用(不配置线程池)
@Service
public class AsyncService {
@Async
public void hello() {
System.out.println("**start**");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程【" + Thread.currentThread().getName() + "】已结束");
}
}
@RestController
public class AsyncController {
@Resource
AsyncService asyncService;
@GetMapping("/hello")
public String hello() {
System.out.println("当前请求的线程名称为:【" + Thread.currentThread().getName() + "】");
asyncService.hello();
return "success";
}
}
然后再在主入口加注解 @EnableAsync 即可。
现在访问localhost:3000/hello时,页面先返回success,然后再在后台打印其他。
注意:如果某个异步方法(标注了@Async)调用同一个类下的异步方法,那@Async会失效的。
默认情况下@Async注解使用的是SimpleAsyncTaskExecuor,但是这个不是真的线程池,这个类不会重用线程,每次调用都会创建一个新线程。
SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方。
ConcurrentTaskExecutor: Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
ThreadPoolTaskScheduler:可以使用cron表达式。
ThreadPoolTaskExecutor : 最常使用,【推荐】。其实质是对java.util.concurrent.ThreadPoolExecutor的包装。
作者:泓落飞涯
链接:https://www.jianshu.com/p/1bb51607dd9f
配置线程池
Spring中存在接口AsyncConfigurer该接口就是用来配置异步线程池的接口,它有两个方法,getAsyncExecutor和getAsyncUncaughtExceptionHandler,第一个方法是获取一个线程池,第二个方法是用来处理异步线程中发生的异常,需要实现。
前面的代码不改,只是在加一个AsyncConfig类,内容为
@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig implements AsyncConfigurer{
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// 核心线程数
taskExecutor.setCorePoolSize(10);
// 最大线程数
taskExecutor.setMaxPoolSize(30);
// 线程队列最大线程数
taskExecutor.setQueueCapacity(2000);
// 初始化线程池
taskExecutor.initialize();
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params)->{
log.error("Error occurs in async method:{}", ex.getMessage());
};
// 应返回一个函数式接口,这里使用了lambda表达式简写
}
}
输出结果:
还有其他常用参数:
- keepAliveTime:超过corePoolSize数量的线程最大空闲(等待)时间,默认60s
也就是说,当poolSize>corePoolSize时,如果一个线程的等待时间超过这个值,就会被销毁。 - allowCoreThreadTimeOut:销毁机制。如果为true,则线程数量销毁到0个。默认是false。
- ThreadNamePrefix:线程池前缀名
- RejectedExecutionHandler:拒绝处理的策略
AbortPolicy:抛出 RejectedExecutionException
CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程执行
DiscardOlderstPolicy:抛弃最老的未处理请求,然后重试 execute
DiscardPolicy:直接抛弃
带返回值的异步方法
一般是将异步方法的返回值使用接口Future、ListenableFuture或类AsyncResult进行包装,也就是将返回值作为泛型传入到上述接口或类中。
Future接口
public interface Future<V> {
// 取消任务。如果取消成功返回true。mayInterruptIfRunning为true表示可以取消正在执行中的任务。如果任务已完成,
// 则一律返回false;如果任务还未执行,则一律返回true
boolean cancel(boolean mayInterruptIfRunning);
// 表示任务是否被取消成功
boolean isCancelled();
// 表示任务是否已完成
boolean isDone();
// 获取执行结果。这个方法会产生阻塞,会一直等到任务执行完毕才返回
V get() throws InterruptedException, ExecutionException;
// 获取执行结果。如果在指定时间内,还未获取到结果,就直接返回null
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
ListenableFuture接口
public interface ListenableFuture<T> extends Future<T> {
void addCallback(ListenableFutureCallback<? super T> callback);
void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback);
default CompletableFuture<T> completable() {
CompletableFuture<T> completable = new DelegatingCompletableFuture<>(this);
addCallback(completable::complete, completable::completeExceptionally);
return completable;
}
}
ListenableFuture继承了Future接口,还额外添加了三个方法,主要用来添加异步现场的回调,可以用来处理异常和获取异步方法返回值。AsyncResult类实现了ListenableFuture接口。
对于Java1.8,其实更加推荐使用CompletableFuture或者guava的ListenableFuture,功能更多。
我的项目里用了 org.springframework.util.concurrent.ListenableFuture,暂时没用那两个。
service层
@Async
public ListenableFuture<String> returnMsg() {
// https://cloud.tencent.com/developer/article/1609499
System.out.println(Thread.currentThread().getName());
String message = "Async Method Result";
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return new AsyncResult<>(message);
}
controller层
@GetMapping("/page2")
public String asyncPage2() {
System.out.println("当前请求的线程名称为:【" + Thread.currentThread().getName() + "】");
ListenableFuture<String> result = asyncService.returnMsg();
result.addCallback(new SuccessCallback<String>() {
@Override
public void onSuccess(String result) {
System.out.println("结果是:" + result);
}
}, new FailureCallback() {
@Override
public void onFailure(Throwable ex) {
log.error("发生了异常:" + ex.getMessage());
}
});
return "async";
}
输出结果: