CompletableFuture使用咨询:Sakila库电影数据并行加载实现
问题分析与优化方案
你的代码存在几个关键问题,会影响并行效率和资源利用率:
- 先启动所有异步任务再查询Film,若Film不存在,前面的异步请求完全是资源浪费
filmCategories任务中在stream映射时直接调用categories.join(),会阻塞该异步线程,破坏并行性- 未处理
join()可能抛出的异常(比如InterruptedException、ExecutionException)
以下是符合你需求的优化实现:
优化思路
- 优先检查Film是否存在:先加载Film,不存在直接返回,避免后续无用的异步操作
- 真正并行执行依赖任务:Film存在后,同时启动
categories、languages、filmCategories三个异步任务,确保任务间的依赖不阻塞并行流程 - 统一处理结果合并:所有异步任务完成后,再将数据合并到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
相关产品推荐
相关产品推荐

