网站首页 > 精选教程 正文
前言
SpringBoot想必大家都用过,但是大家平时使用发布的接口大都是同步的,那么你知道如何优雅的实现异步呢?
这篇文章就是关于如何在Spring Boot中实现异步行为的。但首先,让我们看看同步和异步之间的区别。
- 同步编程:在同步编程中,任务一次执行一个,只有当一个任务完成时,下一个任务才会被解除阻塞。
- 异步编程:在异步编程中,可以同时执行多个任务。您可以在上一个任务完成之前转到另一个任务。
在Spring Boot中,我们可以使用@Async注解来实现异步行为。
实现步骤
- 定义一个异步服务接口AsyncService.java
public interface AsyncService {
void asyncMethod() throws InterruptedException;
Future<String> futureMethod() throws InterruptedException;
}
- 实现定义的接口AsyncServiceImpl.java
@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService {
@Async
@Override
public void asyncMethod() throws InterruptedException {
Thread.sleep(3000);
log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
}
@Async
@Override
public Future<String> futureMethod() throws InterruptedException {
Thread.sleep(5000);
log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
return new AsyncResult<>("task Done");
}
}
- AsyncServiceImpl 是一个 spring 管理的 bean。
- 您的异步方法必须是公共的,而且是被@Async注解修饰。
- 返回类型被限制为 void 或 Future。
- 定义一个控制器AsyncController.java
@EnableAsync
@RestController
@Slf4j
public class AsyncController {
@Autowired
AsyncService asyncService;
@GetMapping("/async")
public String asyncCallerMethod() throws InterruptedException {
long start = System.currentTimeMillis();
log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
asyncService.asyncMethod();
String response = "task completes in :" +
(System.currentTimeMillis() - start) + "milliseconds";
return response;
}
@GetMapping("/asyncFuture")
public String asyncFuture() throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
Future<String> future = asyncService.futureMethod();
// 阻塞获取结果
String taskResult = future.get();
String response = taskResult + "task completes in :" +
(System.currentTimeMillis() - start) + "milliseconds";
return response;
}
}
- 关键点,需要添加启用异步的注解@EnableAsync ,当然这个注解加在其他地方也ok得。
- 当外部调用该接口时,asyncMethod() 将由默认任务执行程序创建的另一个线程执行,主线程不需要等待完成异步方法执行。
- 运行一下
现在我们运行一下看看,是不是异步返回的。
- 可以看到调用/async接口,最终一步调用了方法。
- 调用/asyncFuture,发现返回5秒多,难道不是异步的吗?其实也是异步的,看日志可以看出来,只不过我们返回的是Future,调用Futrue.get()是阻塞的。
自定义异步任务执行器和异常处理
我们现在看看如果异常方法中报错了会怎么样?修改异步代码如下所示,会抛运行时异常:
再次执行异步接口,如下所示,会使用默认的线程池和异常处理。
我们也可以自定义异步方法的处理异常和异步任务执行器,我们需要配置 AsyncUncaughtExceptionHandler,如下代码所示:
@Configuration
public class AsynConfiguration extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new
ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("asyn-task-thread-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler
getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex,
Method method, Object... params) {
System.out.println("Exception: " + ex.getMessage());
System.out.println("Method Name: " + method.getName());
ex.printStackTrace();
}
};
}
}
再次运行,得到的结果如下:
@Async如何工作的?
必须通过使用 @EnableAsync注解注解主应用程序类或任何直接或间接异步方法调用程序类来启用异步支持。主要通过代理模式实现,默认模式是 Proxy,另一种是 AspectJ。代理模式只允许通过代理拦截调用。永远不要从定义它的同一个类调用异步方法,它不会起作用。
当使用 @Async对方法进行注解时,它会根据“proxyTargetClass”属性为该对象创建一个代理。当 spring 执行这个方法时,默认情况下它会搜索关联的线程池定义。上下文中唯一的 spring 框架 TaskExecutor bean 或名为“taskExecutor”的 Executor bean。如果这两者都不可解析,默认会使用spring框架SimpleAsyncTaskExecutor来处理异步方法的执行。
总结
在本文中,我们演示了在 spring boot 中如何使用 @Async 注解和异步方法中的异常处理实现异步行为。我们可以在一个接口中,需要访问不同的资源,比如异步调用各个其他服务的接口,可以使用@Async,然后将结果通过Future的方式阻塞汇总,不失为一个提高性能的好方法。
- 上一篇: 如何在 Java 中控制多个线程的执行顺序?详细介绍一下?
- 下一篇: PHP实现异步调用的方式
猜你喜欢
- 2024-11-19 你知道@Async 是怎么让方法异步执行的吗?
- 2024-11-19 Java 并发编程 11 - 异步执行框架 Executor
- 2024-11-19 Java中异步复用io
- 2024-11-19 JAVA异步方法笔记
- 2024-11-19 面试官:Redis分布式锁超时了,任务还没执行完怎么办?
- 2024-11-19 万能通用的异步实战方案,设计多线程,mq
- 2024-11-19 Java客户端Jedis 对Redis的几种调用方式包括事务、管道、分布式
- 2024-11-19 Dubbo 2.7新特性之异步化改造
- 2024-11-19 新手也能看懂的 SpringBoot 异步编程指南
- 2024-11-19 SpringBoot 异步编程
你 发表评论:
欢迎- 04-11Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- 04-11Java中你知道几种从字符串中找指定的字符的数量
- 04-11探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- 04-11Python字符串详解与示例(python字符串的常见操作)
- 04-11java正则-取出指定字符串之间的内容
- 04-11String s1 = new String("abc");这句话创建了几个字符串对象?
- 04-11java判断字符串中是否包含某个字符
- 04-11关于java开发中正确的发牌逻辑编写规范
- 最近发表
-
- Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- Java中你知道几种从字符串中找指定的字符的数量
- 探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- Python字符串详解与示例(python字符串的常见操作)
- java正则-取出指定字符串之间的内容
- String s1 = new String("abc");这句话创建了几个字符串对象?
- java判断字符串中是否包含某个字符
- 关于java开发中正确的发牌逻辑编写规范
- windows、linux如何后台运行jar(并且显示进程名)
- 腾讯大佬私人收藏,GitHub上最受欢迎的100个JAVA库,值得学习
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)