使用多线程配合线程池提高接口的响应速度

阅读本文前可以先阅读我的另一篇博文:Java面试篇(线程池相关专题)

1. 案例引入

在一个电商网站中,当用户查询订单时,一般会包含三部分信息:

  1. 订单信息
  2. 包含的商品
  3. 物流信息

这三部分信息需要通过不同的微服务来获取,我们该如何完成这个业务呢

2. 常规方案

2.1 示例图

我们先来看一下常规的方案,先查询订单信息、再查询商品信息、最后查询物流信息,整个流程中每个部分是串行化执行的

在这里插入图片描述

2.2 示例代码

在这里插入图片描述

import java.util.ArrayList;
import java.util.List;

public class SingleThreadQuery {
    
    

    /**
     * 模拟从数据库获取订单详情并计算执行时间
     *
     * @throws InterruptedException 如果在睡眠过程中中断
     */
    public static void main(String[] args) throws InterruptedException {
    
    
        // 记录开始时间
        long startTime = System.currentTimeMillis();

        // 初始化订单详情列表
        List<String> orderDetails = new ArrayList<>();
        // 模拟从三个不同的微服务获取数据
        orderDetails.add(fetchDataFromDatabase(1));
        orderDetails.add(fetchDataFromDatabase(2));
        orderDetails.add(fetchDataFromDatabase(3));

        // 输出订单详情
        System.out.println("SingleThreadQuery orderDetails = " + orderDetails);

        // 记录结束时间
        long endTime = System.currentTimeMillis();
        // 计算并输出耗时
        System.out.println("SingleThreadQuery 耗时:" + (endTime - startTime) / 1000 + "秒" + (endTime - startTime) % 1000 + "毫秒");
    }

    private static String fetchDataFromDatabase(Integer type) throws InterruptedException {
    
    
        if (type == 1) {
    
    
            Thread.sleep(500);
            return "订单信息";
        }

        if (type == 2) {
    
    
            Thread.sleep(800);
            return "商品信息";
        }

        Thread.sleep(500);
        return "物流信息";
    }

}

2.3 代码执行耗时

在这里插入图片描述

3. 使用多线程的方案

  • 采用多线程的方案,需要使用 Future 接口

  • 线程池的 submit 方法执行后会返回一个结果,结果的类型为 Future 接口,调用 Future 接口的 get 方法能够获取到线程执行任务后的返回值

    扫描二维码关注公众号,回复: 17431027 查看本文章
  • 值得注意的是,调用 get 方法时会阻塞当前线程,直到成功获取线程执行任务后的返回值

3.1 示例图

我们再来看使用多线程的方案,查询订单信息、查询商品信息、查询物流信息三个操作相当于同时进行,整个流程中每个部分是并发执行的

在这里插入图片描述

3.2 示例代码

在这里插入图片描述

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MultipleThreadQuery {
    
    

    /**
     * 本程序通过固定大小的线程池异步获取数据库数据,并计算总耗时
     *
     * @throws InterruptedException 如果线程被中断
     * @throws ExecutionException   如果Future.get()方法执行异常
     */
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    
    
        // 记录开始时间
        long startTime = System.currentTimeMillis();

        // 初始化存储订单详情的列表
        List<String> orderDetails = Collections.synchronizedList(new ArrayList<>());
        // 初始化存储Future对象的列表,用于获取异步计算结果
        List<Future<String>> futureList = new ArrayList<>();

        // 创建固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        // 提交三个任务到线程池异步执行
        futureList.add(executorService.submit(() -> fetchDataFromDatabase(1)));
        futureList.add(executorService.submit(() -> fetchDataFromDatabase(2)));
        futureList.add(executorService.submit(() -> fetchDataFromDatabase(3)));

        // 遍历Future列表,获取异步计算结果,并添加到订单详情列表中
        for (Future<String> future : futureList) {
    
    
            orderDetails.add(future.get());
        }

        // 打印订单详情
        System.out.println("MultipleThreadQuery orderDetails = " + orderDetails);

        // 记录结束时间
        long endTime = System.currentTimeMillis();
        // 计算并打印耗时
        System.out.println("MultipleThreadQuery 耗时:" + (endTime - startTime) / 1000 + "秒" + (endTime - startTime) % 1000 + "毫秒");

        // 关闭线程池,不再接受新任务,等待所有已提交的任务完成
        executorService.shutdown();
    }

    private static String fetchDataFromDatabase(Integer type) throws InterruptedException {
    
    
        if (type == 1) {
    
    
            Thread.sleep(500);
            return "订单信息";
        }

        if (type == 2) {
    
    
            Thread.sleep(800);
            return "商品信息";
        }

        Thread.sleep(500);
        return "物流信息";
    }

}

3.3 代码执行耗时

在这里插入图片描述

4. 常规方案和使用多线程方案的对比

在这里插入图片描述

在这里插入图片描述

可以看到,使用多线程的方案的耗时远远小于常规方案

5. 注意事项

  1. 在汇总结果时,要确保用于汇总结果的集合是线程安全的,避免出现与线程安全相关的问题
  2. 一般使用多线程提高接口的响应速度只用于纯查询的操作,如果是涉及到修改数据的操作,不建议使用多线程,因为修改数据操作一般会涉及到事务,在多线程的情况下,事务管理将变得不可控
  3. 在项目中使用多线程时,一般是配合线程池使用,如果线程池中的线程都被其它耗时较长的业务占用了,将会出现一些意想不到的问题

猜你喜欢

转载自blog.csdn.net/m0_62128476/article/details/143083818