前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >不仅会用@Async,我把源码也梳理了一遍(上)

不仅会用@Async,我把源码也梳理了一遍(上)

作者头像
java思维导图
发布2019-09-24 14:16:50
8.1K0
发布2019-09-24 14:16:50
举报
文章被收录于专栏:java思维导图java思维导图

说起异步化,很多人会想起异步线程、消息队列等,消息队列不是文章的主题,今天我们来聊聊spring对异步化的支持@EnableAsync&@Async。

我会分为以下几个步骤去说明,首先说说用法,然后再从源码层分析@Async的底层原理,同学们准备好了吗?

@Async用法

想要开启异步化,我们就必须要用用到@EnableAsync注解,这又是我们之前给大家说个的@EnableXXX的模块,大家可以回顾一下:《导图梳理springboot手动、自动装配,让springboot不再难懂》,配置类中开启了@EnableAsync之后,@Async的注解就开始起作用了,我们先来做个简单的测试:

配置类

  • com.example.demo.config.AsyncConfig
代码语言:javascript
复制
@EnableAsync
@Configuration
public class AsyncConfig {

}

一个页面控制器

  • com.example.demo.controller.AsyncController
代码语言:javascript
复制
@Slf4j
@RestController
public class AsyncController {

    @Autowired
    UserService userService;

    @GetMapping("/tt")
    public Object tt() throws InterruptedException {

        String username = userService.findUserNameById(1L);
        log.info("AsyncController----------->" + Thread.currentThread().getName());
        log.info("username---------------->" + username);
        return username;
    }
}

然后就是业务类

  • com.example.demo.service.impl.UserServiceImpl
代码语言:javascript
复制
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Async
    public String findUserNameById(Long id) {

        log.info("UserServiceImpl----------->" + Thread.currentThread().getName());
        return "关注公众号:java思维导图";
    }
}

好了,最简单的用法已经完成了,我在UserServiceImpl#findUserNameById方法上添加了@Async注解,表示此方法是个异步方法。我们先来看看执行结果,然后再来说说上面的程序有什么问题。

控制台输出:

可以看出,两个方法执行的线程是不一样的,明显UserServiceImpl的findUserNameById方法是另起了一个线程task-1执行。

这里我们注意到了一个问题,controller中我们获取不到了异步方法的返回值username了。所以想要获取@Async注解之后的异步方法就不能使用这种方式了,所以我们改下程序:

service中新添加个方法:

代码语言:javascript
复制
@Async
public Future<String> findLastNameById(Long id) {

    log.info("UserServiceImpl----------->" + Thread.currentThread().getName());

    return new AsyncResult<String>("关注公众号:java思维导图");
}

然后controller改成这样:

代码语言:javascript
复制
@GetMapping("/tt")
public Object tt() throws InterruptedException, ExecutionException {

    String username = userService.findUserNameById(1L);
    log.info("AsyncController----------->" + Thread.currentThread().getName());
    log.info("username---------------->" + username);


    Future<String> future = userService.findLastNameById(1L);
    String lastname = future.get();
    log.info("lastname---------------->" + lastname + Thread.currentThread().getName());

    return username;
}

测试结果如下:

可以看到task-1获取不到结果,task-2获取到结果了,但是这里有个问题,为了获取异步的结果,线程http-nio-8080-exec-1一直在等待future.get()的返回结果,才继续往下走,这就有点影响主线程的性能了。

那如果异步方法出现了异常,我们怎么去捕捉呢,一般全局异常处理我们都是处理controller中的异常,但这个异步线程的异常能不能捕捉到都是个问题。其实,对于@Async的异常,是可以配置的。AsyncConfig中我们可以继承一个AsyncConfigurer,然后重写两个方法:

  • com.example.demo.config.AsyncConfig
代码语言:javascript
复制
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {

        //线程池
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setQueueCapacity(25);
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

一个定义线程池,一个定义异常处理器,线程池我们就不说了,这个默认的SimpleAsyncUncaughtExceptionHandler没啥东西:

代码语言:javascript
复制
public class SimpleAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {

   private static final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class);


   @Override
   public void handleUncaughtException(Throwable ex, Method method, Object... params) {
      if (logger.isErrorEnabled()) {
         logger.error("Unexpected exception occurred invoking async method: " + method, ex);
      }
   }

}

所以如果我们想自己处理这个出现异常之后的逻辑,还得重写AsyncUncaughtExceptionHandler 的handleUncaughtException接口,里面有method、params等参数,可以进行重试、或者其他处理等。

异步化原理

好吧,下一篇再写了,今天先到这里,下午还有点事~

待续~

结束语

坚持原创的第三篇(20190920),打卡打卡。希望你们会喜欢。

我是吕一明,欢迎关注我的公众号:java思维导图

做个小调查:

留言区留言:你们公司是用什么方式实现异步处理业务的?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java思维导图 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • @Async用法
  • 异步化原理
  • 结束语
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档