记一次使用CompletableFuture进行简易的接口性能优化

90 阅读2分钟

业务背景为:开发一个刷题平台,题目分类表记录题目分类例如:“JAVA基础”,“数据库基础”。 题目标签表记录题目标签例如:“索引”,“事务”,分类与标签为多对多关系,创建中间表对两表进行关联。 现在需要查询出所有题目分类以及分类相关的标签,将其存入缓存中并展示。如分类1:标签1,标签2,
分类2:标签3,标签4,原接口实现为传入一个分类id

  1. 查询该分类所有下级分类;
  2. 通过下级分类ID进入中间表去查询分类所关联的标签;
  3. 再通过标签的id查询出实际的标签;
    该过程中因数据量不大,在第二步时我选择使用循环查询分类相关的标签,查询后放入缓存。
    这里用一段简易代码来进行展示.
Public List<CategoryBO> QueryLabel(int CategoryId){
        //获取下级分类
        List<Category> categorys = GetLowLevelCategory(CategoryId);
        List<CategoryBO> categoryBOs = new ArrayList<CategoryBO>();
       //原实现 
       for(Category category :categorys){
           CategoryBO categoryBo = new CategoryBO();
           List<Label> labels = GetLabels(category.getCategoryId());
            //将集合放入CategoryBO对象中
            categoryBO.setLabelList(labels);
            categoryBO.setCategoryId(category.getCategoryId());
            categoryBOs.add(categoryBO);
       }
       return categoryBOs;
}

原实现中在循环中去查询标签,查完一个继续查询第二个。这样的效率是很低下的。 查询标签集合这一步是没有同步需求的,可以并发执行。
这个时候可以引入多线程,以及进行异步查询来优化这些操作。这里选择CompletableFuture进行实现。

Public List<CategoryBO> QueryLabel(int CategoryId){
   List<CategoryBO> categoryBOs = new ArrayList<CategoryBO>();
    List<CompletableFuture<List<Label>>> completableFutureList = categorys.stream()
    .map(category ->
            //通过下方方法提交异步任务,其中参数一为函数,传入我们要执行的方法任务,
            //参数二为自定义重写的线程池,只修改了打印的内容,用来标识是哪个线程池在进行执行任务、
            CompletableFuture.supplyAsync(
            () -> GetLabels(category.getCategoryId()),
            labelThreadPool)
    ).collect(Collectors.toList());
    completableFutureList.forEach(future -> {
        try {
            //获取任务执行后返回的结果
           List<Label> result= future.get();
            //进行数据处理,将数据集合返回
                …………   
            // 或者在上方传入的函数中直接进行数据处理,将需要的最终数据直接返回。
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

同时CompletableFuture也可以进行异步回调,多任务组合,达成类似“同步”意义的效果,这里暂不作举例。