项目中使用多线程优化查询多表操作组装数据

使用多线程优化查询多表操作组装数据

使用多线程优化下面的代码

// 查询用户信息
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 实现优化

通过多线程优化你给出的代码,目的是提高查询数据的效率,当查询涉及到多个表的数据时。我们可以使用并行的方式同时查询 UserUserDetail 表,然后将它们的结果合并。使用 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;
}

关键点:

  1. 异步查询:通过 CompletableFuture.supplyAsync() 来进行异步查询 UserUserDetail
  2. thenCombine:当两个异步任务都完成时,thenCombine 会将 UserUserDetail 的结果结合到 userInfoVo 中。
  3. 异常处理:使用 try-catch 捕获 InterruptedExceptionExecutionException,以保证代码在并发执行时能处理可能出现的错误。
  4. 性能提升:两个查询操作是并行执行的,而不是顺序执行,这能减少查询所需的总时间。

通过并行查询,能够在较短的时间内完成数据的获取和合并,提升系统性能,特别是当 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;
}

关键点:

  1. CountDownLatch 初始化:初始化时设置计数为 2,因为我们有两个查询操作(UserUserDetail)。
  2. 查询线程:分别为 UserUserDetail 创建两个线程。每个线程完成一个查询后,调用 latch.countDown(),将计数减1。
  3. latch.await():主线程通过 latch.await() 等待两个查询线程完成。当计数器为 0 时,主线程会继续执行。
  4. 数据合并:两个查询完成后,我们在主线程中将查询结果合并到 UserInfoVo 对象中。

优势:

  • 通过 CountDownLatch,你确保了两个查询操作并行执行,而不会阻塞主线程。
  • 在主线程中等待 CountDownLatch 计数为 0,确保所有查询完成后再处理合并数据。
  • 相比传统的串行查询,这种方式显著减少了等待时间,尤其是在查询量较大的情况下。

适用场景:

CountDownLatch 特别适用于这种“等待多个线程完成”的场景,可以保证多个线程执行完成后再做统一处理。如果你需要更细粒度的控制,CountDownLatch 是一个非常实用的工具。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昔我往昔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值