@Async的使用、应用场景

@Async的作用

正常方法被调用时是同步执行,而@Async标识的方法调用时是异步执行。

应用场景

通常用于耗时较长或者不需要立即得到执行结果的逻辑,说白了就是异步执行
例如:

  1. 发送邮件
  2. 导出数据
  3. 同步商品

@Async的使用

这里就同步商品为例,简单说下商品同步的业务便于理解

假设现在有个SaaS化的商城有个商品总库,总库新增了商品,每个企业的商城可以自主同步总库所新增的商品,这个商品同步过程就可以使用@Async异步去实现。

闲话不多说,直接上代码。

项目目录结构:
在这里插入图片描述
要使用@Async首先需要开启该功能,在配置类下加入@EnableAsync

@Configuration
@EnableAsync
public class AppConfig {
}

商品同步接口(按照规范来说Controller中不应该存在业务逻辑,这里为了便于理解,写的可能不是那么规范)

@RestController
@RequiredArgsConstructor
@RequestMapping("/product")
@Slf4j
public class ProductController {

    private final ProductService productService;

    /**
     * 模拟同步商品  测试异步执行(无返回值)
     * @return
     */
    @GetMapping("/syncProduct")
    public ResultVo<String> syncProduct() {
        log.info("syncProduct invoke start");
        productService.syncProduct();
        log.info("syncProduct invoke end");
        return ResultVo.success();
    }

    /**
     * 测试异步执行(有返回值)
     * @return
     */
    @GetMapping("/testAsyncResult")
    public ResultVo<String> testAsyncResult() {
        log.info("syncProduct invoke start");
        Future<String> stringFuture = productService.testAsyncResult();
        try {
            String result = stringFuture.get();
            log.info(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        log.info("syncProduct invoke end");
        return ResultVo.success();
    }

}

上方包含两个接口

syncProduct:模拟商品同步,使用@Async实现异步执行,没有返回值的情况
testAsyncResult:使用@Async实现异步执行,有返回值的情况

商品Service接口

public interface ProductService {
    void syncProduct();
    Future<String> testAsyncResult();
}

商品Service接口实现

@Service
@Slf4j
public class ProductServiceImpl implements ProductService {

    @Async
    @Override
    public void syncProduct() {
        log.info("同步商品开始");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("同步商品结束");
    }

    @Async
    @Override
    public Future<String> testAsyncResult() {
        log.info("testAsyncResult开始");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("testAsyncResult结束");
        return new AsyncResult<>("执行完成咯!");
    }
}
结论
  1. 调用syncProduct时输出

    2021-09-01 19:43:33.123 INFO 28064 — [nio-6659-exec-1] c.f.d.a.controller.ProductController : syncProduct invoke start
    2021-09-01 19:43:33.129 INFO 28064 — [nio-6659-exec-1] c.f.d.a.controller.ProductController : syncProduct invoke end
    2021-09-01 19:43:33.129 INFO 28064 — [ task-1] c.f.d.a.service.impl.ProductServiceImpl : 同步商品开始
    2021-09-01 19:43:38.141 INFO 28064 — [ task-1] c.f.d.a.service.impl.ProductServiceImpl : 同步商品结束

    1. 可以看到Service中打印日志和Controller中打印日志使用了不一样的线程

    2. 观察打印日志时间可以看出syncProduct invoke start 和syncProduct invoke end 打印的间隔不到10毫秒,很明显已经异步执行了。

    3. 不需要等 productService.syncProduct 方法执行完,用户已经收到响应接口

      {
      “status”: true,
      “code”: “200”,
      “message”: “成功!”,
      “data”: null
      }

      由此得出 productService.syncProduct 确实异步执行了。

  2. 调用testAsyncResult时,有两种情况

    1. 如果使用了stringFuture.get()获取执行结果,那么当前线程会阻塞,直到Service执行结束返回结果才会继续向下执行。
      日志打印:

      2021-09-01 19:56:34.841 INFO 28064 — [nio-6659-exec-4] c.f.d.a.controller.ProductController : syncProduct invoke start
      2021-09-01 19:56:38.011 INFO 28064 — [ task-2] c.f.d.a.service.impl.ProductServiceImpl : testAsyncResult开始
      2021-09-01 19:56:43.022 INFO 28064 — [ task-2] c.f.d.a.service.impl.ProductServiceImpl : testAsyncResult结束
      2021-09-01 19:56:43.023 INFO 28064 — [nio-6659-exec-4] c.f.d.a.controller.ProductController : 执行完成咯!
      2021-09-01 19:56:43.024 INFO 28064 — [nio-6659-exec-4] c.f.d.a.controller.ProductController : syncProduct invoke end

    2. 如果没有使用stringFuture.get()获取执行结果,和 productService.syncProduct 一样,不会阻塞。
      日志打印:

      2021-09-01 20:01:21.219 INFO 26060 — [nio-6659-exec-1] c.f.d.a.controller.ProductController : syncProduct invoke start
      2021-09-01 20:01:21.223 INFO 26060 — [nio-6659-exec-1] c.f.d.a.controller.ProductController : syncProduct invoke end
      2021-09-01 20:01:21.223 INFO 26060 — [ task-1] c.f.d.a.service.impl.ProductServiceImpl : testAsyncResult开始
      2021-09-01 20:01:26.224 INFO 26060 — [ task-1] c.f.d.a.service.impl.ProductServiceImpl : testAsyncResult结束

疑问解答

到这里可能有的小伙伴有疑问,如果使用stringFuture.get()获取执行结果,那异步不就没意义了?
确实,在这个简单的例子当中确实没意义。但是有些场景就非常有意义了,例如统计数据,一个接口需要统计优惠券使用情况、商品售卖情况、新增成交用户,如果一个一个轮流执行假设需要20秒,相当于是累加每个统计的执行时长。那如果使用@Async去异步执行统计,然后等待每个统计结果都返回了就统计完了,这样统计执行时长顶多就是统计最久的那个业务逻辑,而不是每个统计累加的执行时长。这种情况就充分体现了使用@Async的意义。

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值