-
前言
-
1.@Async异步调用
-
2.手动创建线程池管理@Async异步的线程
-
3.异步创建有返回值的调用
前言
我们在开发中经常会对执行慢的方法或不涉及主业务的方法执行异步调用。
-
实现方式1:可以自己手动创建原生的线程,实现异步。 -
实现方式2:也可以使用springboot的@Async实现异步。
本文主要说下springboot的@Async方式和多线程时如何自定义线程池,以及希望有返回结果时,如何用其内置的方法为我所用。
1.@Async异步调用
异步的原理:springboot会为代理对象创建一个线程,执行异步方法。
-
1.在springboot的入口函数处引入 开启异步自动配置注解 @EnableAsync
。

-
2书写异步方法

特别注意
异步方法不可和调用它的类在一个类中, 因为@Async是springboot使用的代理对象来创建或者使用线程池中的线程处理, 如果
在一个类中就成同步了
。这点和不能调用同一个类的带有@Transactional方法是一个道理。
2.手动创建线程池管理@Async异步的线程
如果不手动创建线程池,springboot会用自己的线程池来处理,不过为了更好的控制线程,我们可以手动创建。springboot也给我们提供了配置方法。我们可以这样配置。
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
/**
* 核心线程数(默认线程数)
*/
private static final int corePoolSize = 10;
/**
* 最大线程数
*/
private static final int maxPoolSize = 50;
/**
* 允许线程空闲时间(单位:默认为秒)
*/
private static final int keepAliveTime = 5;
/**
* 缓冲队列大小
*/
private static final int queueCapacity = 200;
/**
* 线程池名前缀
*/
private static final String threadNamePrefix = "Async-Service-";
@Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveTime);
executor.setThreadNamePrefix(threadNamePrefix);
// 线程池对拒绝任务的处理策略
// CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 初始化
executor.initialize();
return executor;
}
}
然后在用到的异步方法的地方,显式引用该线程池。

备注
: 可以指定线程池的前缀threadNamePrefix,便于以后分析查看日志。如果需要创建多个线程池,指定不同的前缀,就可以快速定位出现问题的代码。
3.异步创建有返回值的调用
有时候我们不止希望异步执行任务,还希望任务执行完成后会有一个返回值,在java中提供了Future泛型接口,用来接收任务执行结果,springboot也提供了此类支持,使用实现了ListenableFuture接口的类如AsyncResult来作为返回值的载体。比如上例中,我们希望返回一个类型为String类型的值,可以将返回值改造为:异步任务类
@Component
public class AsyncTask {
@Async
public Future<String> task1() throws InterruptedException{
long currentTimeMillis = System.currentTimeMillis();
Thread.sleep(1000);
long currentTimeMillis1 = System.currentTimeMillis();
System.out.println("task1任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");
return new AsyncResult<String>("task1执行完毕");
}
@Async
public Future<String> task2() throws InterruptedException{
long currentTimeMillis = System.currentTimeMillis();
Thread.sleep(2000);
long currentTimeMillis1 = System.currentTimeMillis();
System.out.println("task2任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");
return new AsyncResult<String>("task2执行完毕");
}
@Async
public Future<String> task3() throws InterruptedException{
long currentTimeMillis = System.currentTimeMillis();
Thread.sleep(3000);
long currentTimeMillis1 = System.currentTimeMillis();
System.out.println("task3任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");
return new AsyncResult<String>("task3执行完毕");
}
}
任务返回后使用:
@RequestMapping("")
@RestController
public class AsyncTaskController {
@Autowired
private AsyncTask asyncTask;
@RequestMapping("")
public String doTask() throws InterruptedException{
long currentTimeMillis = System.currentTimeMillis();
Future<String> task1 = asyncTask.task1();
Future<String> task2 = asyncTask.task2();
Future<String> task3 = asyncTask.task3();
String result = null;
for (;;) {
if(task1.isDone() && task2.isDone() && task3.isDone()) {
// 三个任务都调用完成,退出循环等待
break;
}
Thread.sleep(1000);
}
long currentTimeMillis1 = System.currentTimeMillis();
result = "task任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms";
return result;
}
}
AsyncTaskController 类中,如果想得到结果,可以使用task1.get()方法,这个方法是阻塞的。如果想实现 多个结果依赖如累加等,可以下面这样写
String s1 = task1.get();
String s2 = task2.get();
String s2 = task3.get();
String s = s1 + s2 + s3;
return s; //切记不可 return task1.get() + task2.get() +task3.get();
开通了个微信公众号:
搜索:怒放de每一天
关注订阅号,了解更多,期待和大家一起成长!!
大功告成,完!
原文始发于微信公众号(怒放de每一天):使用spring boot的@Async实现异步调用和线程池复用
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/106829.html