剑指慢服务——使用并发

4 阅读2分钟

并发与并行的区别

1. 定义区别

  • 并发(Concurrency)

    • 表示多个任务在同一时间段内交替执行,但不一定同时运行。
    • 核心在于任务的逻辑调度
    • 比喻:一个服务员为多个桌子上的顾客服务,他快速切换去满足不同顾客的需求。
  • 并行(Parallelism)

    • 表示多个任务在同一时刻同时运行,通常需要多核处理器的支持。
    • 核心在于任务的物理同时性
    • 比喻:多个服务员分别为不同的桌子同时服务。

2. 实现方式

  • 并发

    • 通过操作系统的线程调度机制实现,一个核心通过快速切换任务来模拟同时执行。
    • 硬件需求:单核或多核处理器均可。
    • 语言支持:例如 Java 的线程池、协程,Python 的 asyncio。
  • 并行

    • 通过多核或多处理器来实现多个任务同时执行。
    • 硬件需求:多核或分布式处理器。
    • 技术支持:如 CUDA(GPU并行计算)、MPI(分布式并行计算)。

优化方案

场景:前端一个页面需要同时调用后台三个接口,每个接口时间都不快,导致页面响应时间慢,需要进行优化。

方案:分析每个接口的调用时长,看各个接口内部是否还有优化空间;同时将前端内部串行调用改成并发调用,使用自定义线程池+CompletableFuture类。

Controller层代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

@RestController
public class ConcurrentController {

    @Autowired
    private ConcurrentService concurrentService;

    @GetMapping("/startConcurrentTask")
    public String startConcurrentTask() throws ExecutionException, InterruptedException {
        //获取当前时间
        long startTime = System.currentTimeMillis();
        // 调用三个异步服务
        CompletableFuture<String> future1 = concurrentService.callService1();
        CompletableFuture<String> future2 = concurrentService.callService2();
        CompletableFuture<String> future3 = concurrentService.callService3();

        // 等待所有异步任务完成并合并结果
        CompletableFuture<String> combinedFuture =  CompletableFuture.allOf(future1, future2, future3)
                .thenApply(v -> {
                    try {
                        String result1 = future1.get();
                        String result2 = future2.get();
                        String result3 = future3.get();
                        return combineResults(result1, result2, result3);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                });
        // 获取最终结果
        String finalResult = combinedFuture.get();
        System.out.println("Final Result: " + finalResult);

        // 计算总耗时
        long endTime = System.currentTimeMillis();
        long totalTime = endTime - startTime;
        System.out.println("Total Time: " + totalTime + " milliseconds");

        return finalResult;
    }

    private String combineResults(String result1, String result2, String result3) {
        return result1 + ", " + result2 + ", " + result3;
    }


}

Service层代码

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Service
public class ConcurrentService {

    @Async("taskExecutor") // 使用自定义线程池
    public CompletableFuture<String> callService1() {
        try {
            Thread.sleep(1000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("Result from Service 1");
    }

    @Async("taskExecutor")
    public CompletableFuture<String> callService2() {
        try {
            Thread.sleep(1500); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("Result from Service 2");
    }

    @Async("taskExecutor")
    public CompletableFuture<String> callService3() {
        try {
            Thread.sleep(1200); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("Result from Service 3");
    }
}

配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@EnableAsync  // 启用异步支持
public class AsyncConfig {

    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("async-task-");
        executor.initialize();
        return executor;
    }
}

需要注意的问题

1. 线程池的大小如何确定?

2. 任务处理完是否需要手动关闭线程池?

3. 如何选择合适的线程提交拒绝策略?

4. 接口的吞吐量如何计算?

5. 配合限流做好线上监控