使用多线程优化查询多表操作组装数据
使用多线程优化下面的代码
// 查询用户信息
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class).eq(User::getId, userId);
User user = getOne(queryWrapper);
LambdaQueryWrapper<UserDetail> detailLambdaQueryWrapper = Wrappers.lambdaQuery(UserDetail.class).eq(UserDetail::getId, userId);
UserDetail userDetail = userDetailService.getOne(detailLambdaQueryWrapper);
//组装的新数据
UserInfoVo userInfoVo = new UserInfoVo();
// user 表
userInfoVo.setUserName(user.getUserName());
userInfoVo.setIdentityCode(user.getIdentityCode());
// user_detail 表数据
userInfoVo.setUserCode(userDetail.getUserCode());
方式1:基于 CompletableFuture 实现优化
通过多线程优化你给出的代码,目的是提高查询数据的效率,当查询涉及到多个表的数据时。我们可以使用并行的方式同时查询 User
和 UserDetail
表,然后将它们的结果合并。使用 Java 的 ExecutorService
或者 CompletableFuture
可以非常方便地实现并行查询。
这里我们使用 CompletableFuture
来进行优化,它会使得两个查询异步执行,从而提高效率。
优化后的代码如下:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public UserInfoVo getUserInfo(Long userId) {
// 创建一个新的 UserInfoVo 对象
UserInfoVo userInfoVo = new UserInfoVo();
// 创建两个 CompletableFuture 来进行异步查询
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> {
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class).eq(User::getId, userId);
return getOne(queryWrapper); // 查询 User 表
});
CompletableFuture<UserDetail> userDetailFuture = CompletableFuture.supplyAsync(() -> {
LambdaQueryWrapper<UserDetail> detailLambdaQueryWrapper = Wrappers.lambdaQuery(UserDetail.class).eq(UserDetail::getId, userId);
return userDetailService.getOne(detailLambdaQueryWrapper); // 查询 UserDetail 表
});
// 使用 thenCombine 来合并两个查询结果
CompletableFuture<Void> combinedFuture = userFuture.thenCombine(userDetailFuture, (user, userDetail) -> {
if (user != null) {
userInfoVo.setUserName(user.getUserName());
userInfoVo.setIdentityCode(user.getIdentityCode());
}
if (userDetail != null) {
userInfoVo.setUserCode(userDetail.getUserCode());
}
return null;
});
try {
// 等待所有异步任务执行完成
combinedFuture.get();
} catch (InterruptedException | ExecutionException e) {
// 处理异常
e.printStackTrace();
}
return userInfoVo;
}
关键点:
- 异步查询:通过
CompletableFuture.supplyAsync()
来进行异步查询User
和UserDetail
。 - thenCombine:当两个异步任务都完成时,
thenCombine
会将User
和UserDetail
的结果结合到userInfoVo
中。 - 异常处理:使用
try-catch
捕获InterruptedException
或ExecutionException
,以保证代码在并发执行时能处理可能出现的错误。 - 性能提升:两个查询操作是并行执行的,而不是顺序执行,这能减少查询所需的总时间。
通过并行查询,能够在较短的时间内完成数据的获取和合并,提升系统性能,特别是当 getOne
查询较慢时。
方式2: 基于CountDownLatch 实现优化
使用 CountDownLatch
进行优化也是一种方式,当你希望等待多个线程并发执行完成后再继续后续操作时。CountDownLatch
是一个同步辅助工具类,可以让主线程等待多个子线程执行完成。
我们可以使用 CountDownLatch
来优化代码,在多线程执行查询的同时,保证在两个查询完成之后才将数据合并到 UserInfoVo
对象中。
下面是使用 CountDownLatch
优化的代码:
import java.util.concurrent.CountDownLatch;
public UserInfoVo getUserInfo(Long userId) {
// 创建一个新的 UserInfoVo 对象
UserInfoVo userInfoVo = new UserInfoVo();
// 创建 CountDownLatch,计数为2,表示有两个线程需要完成
CountDownLatch latch = new CountDownLatch(2);
// 用来存储查询结果
User user = null;
UserDetail userDetail = null;
// 查询 User 表
Thread userThread = new Thread(() -> {
try {
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class).eq(User::getId, userId);
user = getOne(queryWrapper); // 查询 User 表
} finally {
latch.countDown(); // 查询完成后,计数减1
}
});
// 查询 UserDetail 表
Thread userDetailThread = new Thread(() -> {
try {
LambdaQueryWrapper<UserDetail> detailLambdaQueryWrapper = Wrappers.lambdaQuery(UserDetail.class).eq(UserDetail::getId, userId);
userDetail = userDetailService.getOne(detailLambdaQueryWrapper); // 查询 UserDetail 表
} finally {
latch.countDown(); // 查询完成后,计数减1
}
});
// 启动两个查询线程
userThread.start();
userDetailThread.start();
try {
// 主线程等待两个查询线程执行完成
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 查询完成后,合并数据
if (user != null) {
userInfoVo.setUserName(user.getUserName());
userInfoVo.setIdentityCode(user.getIdentityCode());
}
if (userDetail != null) {
userInfoVo.setUserCode(userDetail.getUserCode());
}
return userInfoVo;
}
关键点:
- CountDownLatch 初始化:初始化时设置计数为
2
,因为我们有两个查询操作(User
和UserDetail
)。 - 查询线程:分别为
User
和UserDetail
创建两个线程。每个线程完成一个查询后,调用latch.countDown()
,将计数减1。 latch.await()
:主线程通过latch.await()
等待两个查询线程完成。当计数器为0
时,主线程会继续执行。- 数据合并:两个查询完成后,我们在主线程中将查询结果合并到
UserInfoVo
对象中。
优势:
- 通过
CountDownLatch
,你确保了两个查询操作并行执行,而不会阻塞主线程。 - 在主线程中等待
CountDownLatch
计数为0
,确保所有查询完成后再处理合并数据。 - 相比传统的串行查询,这种方式显著减少了等待时间,尤其是在查询量较大的情况下。
适用场景:
CountDownLatch
特别适用于这种“等待多个线程完成”的场景,可以保证多个线程执行完成后再做统一处理。如果你需要更细粒度的控制,CountDownLatch
是一个非常实用的工具。