You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CompletableFuture使用咨询:Sakila库电影数据并行加载实现

问题分析与优化方案

你的代码存在几个关键问题,会影响并行效率和资源利用率:

  • 先启动所有异步任务再查询Film,若Film不存在,前面的异步请求完全是资源浪费
  • filmCategories任务中在stream映射时直接调用categories.join(),会阻塞该异步线程,破坏并行性
  • 未处理join()可能抛出的异常(比如InterruptedException、ExecutionException)

以下是符合你需求的优化实现:

优化思路

  1. 优先检查Film是否存在:先加载Film,不存在直接返回,避免后续无用的异步操作
  2. 真正并行执行依赖任务:Film存在后,同时启动categories、languages、filmCategories三个异步任务,确保任务间的依赖不阻塞并行流程
  3. 统一处理结果合并:所有异步任务完成后,再将数据合并到FilmDto中

优化代码

// 1. 先加载Film,不存在直接返回
return filmRepository.findById(filmId)
        .map(filmEntity -> {
            FilmDto filmDto = mapper.filmToFilmDto(filmEntity);
            Integer filmLanguageId = filmDto.getLanguageId();

            // 2. 并行启动三个异步任务
            CompletableFuture<Map<Integer, String>> categoriesFuture = CompletableFuture.supplyAsync(
                    () -> categoryService.getCategories().stream()
                            .collect(Collectors.toMap(CategoryDto::getCategoryId, CategoryDto::getCategoryName))
            );

            CompletableFuture<Map<Integer, String>> languagesFuture = CompletableFuture.supplyAsync(
                    () -> languageService.getLanguages().stream()
                            .collect(Collectors.toMap(LanguageDto::getLanguageId, LanguageDto::getLanguageName))
            );

            CompletableFuture<List<FilmCategoryDto>> filmCategoriesFuture = CompletableFuture.supplyAsync(
                    () -> filmCategoryRepository.findFilmCategoryEntitiesByFilmId(filmId)
                            .stream()
                            .map(mapper::filmCategoryToFilmCategoryDto)
                            .collect(Collectors.toList())
            );

            // 3. 等待所有任务完成,并合并结果
            try {
                // 等待三个任务全部完成
                CompletableFuture.allOf(categoriesFuture, languagesFuture, filmCategoriesFuture).join();

                // 补全语言名称
                Map<Integer, String> languagesMap = languagesFuture.get();
                filmDto.setLanguageName(languagesMap.get(filmLanguageId));

                // 补全类别名称并设置到FilmDto
                Map<Integer, String> categoriesMap = categoriesFuture.get();
                List<FilmCategoryDto> filmCategories = filmCategoriesFuture.get()
                        .stream()
                        .map(dto -> {
                            dto.setCategoryName(categoriesMap.get(dto.getCategoryId()));
                            return dto;
                        })
                        .collect(Collectors.toList());
                filmDto.setFilmCategories(filmCategories);

                return filmDto;
            } catch (InterruptedException | ExecutionException e) {
                // 处理异步任务异常,可根据业务需求抛出或返回错误结果
                Thread.currentThread().interrupt();
                throw new RuntimeException("Failed to load film related data", e);
            }
        })
        .orElse(null);

额外说明

  • 异常处理:添加了InterruptedException和ExecutionException的捕获,避免未处理的异常导致程序崩溃
  • 并行效率:所有依赖任务真正并行执行,只有在结果合并阶段才会等待任务完成,最大化利用线程资源
  • 资源节省:只有Film存在时才会启动后续异步任务,避免无效的数据库查询
  • 简化逻辑:将FilmCategory的类别名称补全放到结果合并阶段,避免在异步任务内部阻塞

内容的提问来源于stack exchange,提问作者dhinkatika

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 20:55:17