青岛比较知名的网站建设公司/南京百度推广
CompletableFuture
是 Java 8 引入的一个用于异步编程的类,它是 Future 接口的增强版本。以下是主要特点和常用方法:
- 基础创建:
// 创建一个完成的 Future
CompletableFuture<String> cf1 = CompletableFuture.completedFuture("Hello");// 创建一个异步执行的 Future
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {return "Hello Async";
});
- 转换和链式操作:
CompletableFuture.supplyAsync(() -> "Hello").thenApply(s -> s + " World") // 转换值.thenAccept(System.out::println) // 消费值.thenRun(() -> System.out.println("Done")); // 执行操作
- 组合多个 Future:
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> "World");// 组合两个 Future
cf1.thenCombine(cf2, (result1, result2) -> result1 + " " + result2).thenAccept(System.out::println);
- 异常处理:
CompletableFuture.supplyAsync(() -> {throw new RuntimeException("出错了");
})
.exceptionally(throwable -> {System.out.println("发生错误:" + throwable.getMessage());return "默认值";
})
.thenAccept(System.out::println);
- 实际应用示例:
public class Service {public CompletableFuture<String> getDataAsync() {return CompletableFuture.supplyAsync(() -> {// 模拟耗时操作try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}return "数据";});}public void process() {getDataAsync().thenApply(data -> data + "处理").thenAccept(result -> System.out.println("结果: " + result)).exceptionally(ex -> {System.err.println("处理失败: " + ex.getMessage());return null;});}
}
主要方法说明:
supplyAsync()
: 异步执行有返回值的任务runAsync()
: 异步执行无返回值的任务thenApply()
: 转换值(类似 map)thenAccept()
: 消费值(无返回值)thenCombine()
: 组合两个 FutureallOf()
: 等待多个 Future 全部完成anyOf()
: 等待多个 Future 中的任意一个完成
使用建议:
- 优先使用
thenCompose()
而不是thenApply()
来组合返回 CompletableFuture 的方法 - 记得处理异常,可以使用
exceptionally()
或handle()
- 如果需要自定义线程池,可以在创建时指定:
CompletableFuture.supplyAsync(supplier, executor)
这些是 CompletableFuture 的基础用法,它能大大简化异步编程的复杂度。需要注意的是,CompletableFuture 默认使用 ForkJoinPool.commonPool(),在生产环境中可能需要考虑使用自定义的线程池。
项目场景
批量修改信息
1)请求类,接受图片 id 列表等字段:
@Data
public class PictureEditByBatchRequest implements Serializable {/*** 图片 id 列表*/private List<Long> pictureIdList;/*** 空间 id*/private Long spaceId;/*** 分类*/private String category;/*** 标签*/private List<String> tags;private static final long serialVersionUID = 1L;
}
2)开发批量修改图片服务,依次完成参数校验、空间权限校验、图片查询、批量更新操作:
@Override
@Transactional(rollbackFor = Exception.class)
public void editPictureByBatch(PictureEditByBatchRequest pictureEditByBatchRequest, User loginUser) {List<Long> pictureIdList = pictureEditByBatchRequest.getPictureIdList();Long spaceId = pictureEditByBatchRequest.getSpaceId();String category = pictureEditByBatchRequest.getCategory();List<String> tags = pictureEditByBatchRequest.getTags();// 1. 校验参数ThrowUtils.throwIf(spaceId == null || CollUtil.isEmpty(pictureIdList), ErrorCode.PARAMS_ERROR);ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);// 2. 校验空间权限Space space = spaceService.getById(spaceId);ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");if (!loginUser.getId().equals(space.getUserId())) {throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有空间访问权限");}// 3. 查询指定图片,仅选择需要的字段List<Picture> pictureList = this.lambdaQuery().select(Picture::getId, Picture::getSpaceId).eq(Picture::getSpaceId, spaceId).in(Picture::getId, pictureIdList).list();if (pictureList.isEmpty()) {return;}// 4. 更新分类和标签pictureList.forEach(picture -> {if (StrUtil.isNotBlank(category)) {picture.setCategory(category);}if (CollUtil.isNotEmpty(tags)) {picture.setTags(JSONUtil.toJsonStr(tags));}});// 5. 批量更新boolean result = this.updateBatchById(pictureList);ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
}
上述是调用mybatis plus
的 updateBatchById
批量更新的操作。如果要处理大量数据,可以使用线程池 + 分批 + 并发进行优化,参考代码如下:
@Resource
private ThreadPoolExecutor customExecutor;/*** 批量编辑图片分类和标签*/
@Override
@Transactional(rollbackFor = Exception.class)
public void batchEditPictureMetadata(PictureBatchEditRequest request, Long spaceId, Long loginUserId) {// 参数校验validateBatchEditRequest(request, spaceId, loginUserId);// 查询空间下的图片List<Picture> pictureList = this.lambdaQuery().eq(Picture::getSpaceId, spaceId).in(Picture::getId, request.getPictureIds()).list();if (pictureList.isEmpty()) {throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "指定的图片不存在或不属于该空间");}// 分批处理避免长事务int batchSize = 100;List<CompletableFuture<Void>> futures = new ArrayList<>();for (int i = 0; i < pictureList.size(); i += batchSize) {List<Picture> batch = pictureList.subList(i, Math.min(i + batchSize, pictureList.size()));// 异步处理每批数据CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {batch.forEach(picture -> {// 编辑分类和标签if (request.getCategory() != null) {picture.setCategory(request.getCategory());}if (request.getTags() != null) {picture.setTags(String.join(",", request.getTags()));}});boolean result = this.updateBatchById(batch);if (!result) {throw new BusinessException(ErrorCode.OPERATION_ERROR, "批量更新图片失败");}}, customExecutor);futures.add(future);}// 等待所有任务完成CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}
示例图如下