谷粒微服务高级篇学习笔记整理---异步线程池

多线程回顾

多线程实现的4种方式


1. 继承 Thread

通过继承 Thread 类并重写 run() 方法实现多线程。

public class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程运行: " + Thread.currentThread().getName());}
}// 使用
public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程
}

特点

  • 缺点:Java 单继承的限制,无法再继承其他类。
  • 适用场景:简单任务,无需共享资源。

2. 实现 Runnable 接口

实现 Runnable 接口,将任务逻辑写在 run() 方法中。

public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程运行: " + Thread.currentThread().getName());}
}// 使用
public static void main(String[] args) {Thread thread = new Thread(new MyRunnable());thread.start();
}

特点

  • 优点:避免单继承限制,适合资源共享(如多个线程处理同一任务)。
  • 推荐场景:大多数情况下优先使用。

3. 实现 Callable 接口 + Future

通过 Callable 允许返回结果和抛出异常,结合 FutureFutureTask 获取异步结果。

import java.util.concurrent.*;public class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {return "执行结果: " + Thread.currentThread().getName();}
}// 使用
public static void main(String[] args) throws Exception {ExecutorService executor = Executors.newSingleThreadExecutor();Future<String> future = executor.submit(new MyCallable());System.out.println(future.get()); // 阻塞获取结果executor.shutdown();
}

特点

  • 优点:支持返回值和异常处理。
  • 适用场景:需要获取线程执行结果的场景。

4. 使用线程池(Executor 框架)

通过 Executors 工具类创建线程池,统一管理线程资源。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(3);for (int i = 0; i < 5; i++) {executor.execute(() -> {System.out.println("线程运行: " + Thread.currentThread().getName());});}executor.shutdown();}
}

特点

  • 优点:降低资源消耗,提高线程复用率,支持任务队列和拒绝策略。
  • 推荐场景:生产环境首选,高并发任务处理。

对比与建议
方式返回值异常处理灵活性资源消耗
继承 Thread不支持有限
实现 Runnable不支持有限
实现 Callable支持支持
线程池(Executor支持支持最高最低

建议

  • 优先选择 实现 Runnable/Callable 接口,避免继承局限性。
  • 生产环境务必使用 线程池,提升性能并确保稳定性。
  • 需要结果时使用 Callable + Future,简单任务用 Runnable

线程池ExecutorService的7大参数

线程池构造函数

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler
)
参数说明
1. 核心线程数(corePoolSize)
  • 作用:线程池中始终保持存活的线程数量(即使空闲)。
  • 特点
    • 默认情况下,核心线程在空闲时不会销毁(除非设置 allowCoreThreadTimeOut(true))。
2. 最大线程数(maximumPoolSize)
  • 作用:线程池允许创建的最大线程数(包括核心线程和非核心线程)。
  • 规则
    • 当任务队列已满且当前线程数小于最大线程数时,会创建新线程处理任务。
3. 线程存活时间(keepAliveTime + unit)
  • 作用:非核心线程空闲时的存活时间。unit为时间单位
  • 规则
    • 非核心线程在空闲时间超过 keepAliveTime 后会被销毁。
    • 如果 allowCoreThreadTimeOut(true),核心线程也会受此时间限制。
4. 任务队列(workQueue)
  • 作用:用于存放待执行任务的阻塞队列。
  • 常见队列类型
    • 无界队列:如 LinkedBlockingQueue(默认无界,可能导致 OOM)。
    • 有界队列:如 ArrayBlockingQueue(需指定容量)。
    • 同步移交队列:如 SynchronousQueue(不存储任务,直接移交线程)。
5. 线程工厂(threadFactory)
  • 作用:自定义线程的创建方式(如命名、优先级、是否为守护线程等)。
  • 默认实现Executors.defaultThreadFactory()
6. 拒绝策略(handler)
  • 作用:当任务队列已满且线程数达到最大时,如何处理新提交的任务。
  • 常见策略
    • AbortPolicy(默认):抛出 RejectedExecutionException异常,且不会静默丢弃任务
    • CallerRunsPolicy:由提交任务的线程直接执行任务。
    • DiscardPolicy:静默丢弃新任务。
    • DiscardOldestPolicy:丢弃队列中最旧的任务,尝试重新提交新任务。
运行流程
1. 线程池创建,准备好 core 数量的核心线程,准备接受任务2. 新的任务进来,用 core 准备好的空闲线程执行。(1)、core满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行(2)、阻塞队列满了,就直接开新线程执行,最大只能开到max指定的数量(3)、max都执行好了。Max-core 数量空闲的线程会在 keepAliveTime指定的时间后自动销毁。最终保持到 core 大小(4)、如果线程数开到了 max的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理3. 所有的线程创建都是由指定的 factory 创建的。
  1. 优先级顺序:核心线程 → 任务队列 → 非核心线程 → 拒绝策略。
  2. 非核心线程:仅在队列满时创建,空闲超时后销毁
  3. 队列选择
    • 无界队列:可能导致 OOM(如 LinkedBlockingQueue)。
    • 同步队列:适合高并发快速响应(如 SynchronousQueue)。

常见面试问题:

一个线程池中core 7; max 20; quue 50, 100个并发进来怎么分配:
7个会被立即执行,50个进入阻塞队列,再开13个线程进行执行,剩下的30个就使用拒绝策略

常见4种线程池

A、newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
core是0,所有都可回收

B、newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
固定大小,core=max;都不可回收

C、newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。
定时任务的线程池

D、newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序 (FIFO、LIFO、优先级) 执行。
单线程的线程池,后台从队列里面获取任务,挨个执行

为何实际开发中使用线程池

  1. 降低资源的消耗

    通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗

  2. 提高响应速度

    因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行

  3. 提高线程的可管理性

    线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配

CompletableFuture异步编排

1. 简介 & 业务场景

Future 是 Java 5 添加的类,用来描述一个异步计算的结果。可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,也可以使用cancel 方法停止任务的执行。

虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式当计算结果完成及时通知监听者呢?

CompletableFutureJava 8 引入的一个异步编程工具,位于 java.util.concurrent 包中。它结合了 Lambda 表达式以及丰富的 API,使得编写、组合和管理异步任务变得更加简洁和灵活。

  • 主要特点
    • 非阻塞执行:在等待 I/O 或耗时操作时,不会占用主线程,充分利用系统资源。
    • 任务组合:支持将多个任务串行化、并行组合或者以其他灵活的方式进行协同工作。
    • 异常处理:内置了异常感知与处理机制,可以在任务执行过程中捕获并处理异常。
    • 灵活的回调机制:通过丰富的回调方法,可以在任务完成后进行进一步处理。
  • 业务场景
    • 高并发场景:如 Web 应用中同时处理大量请求时,通过异步调用提高吞吐量。
    • 分布式系统:在微服务架构下,多个服务之间的异步通信和数据聚合。
    • I/O 密集型任务:例如文件读写、网络请求、数据库操作等,异步化可以避免线程阻塞。
    • 复杂业务流程:当多个任务存在依赖关系或者需要并行处理后再组合结果时,CompletableFuture 能够简化代码逻辑。

谷粒商城中商品详情页的逻辑较为复杂,涉及远程调用

image-20250328175809003

假如商品详情页的每个查询,需要如上标注的事件才能完成,那么用户需要5.5秒之后才能看到商品详情页的内容,这显然是不可接受的,但是如果有多个线程同时完成这6步操作,也许可以在1.5s内响应


2. 启动异步任务

启动异步任务主要有两种常用方法:

  • supplyAsync
    用于启动有返回结果的异步任务。典型用法如下:

    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {// 执行耗时操作,比如调用远程服务、数据库查询等return "任务结果";
    });
    
  • runAsync
    用于启动没有返回结果的异步任务。例如:

    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {// 执行任务,但不需要返回结果
    });
    
  • 自定义线程池
    可以通过传入自定义的 Executor 来管理线程资源,避免使用默认的 ForkJoinPool,从而更好地控制线程数和任务调度:

    ExecutorService executor = Executors.newFixedThreadPool(10);
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {// 耗时任务return "结果";
    }, executor);
    

3. 回调与异常感知

CompletableFuture 提供了一系列回调方法,方便在任务完成后自动触发后续操作,同时内置了异常处理能力:

  • 常用回调方法

    public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action);
    public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable>action);
    public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable>action,Executor executor);
    public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn) 
    

    whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。

    whenComplete 和 whenCompleteAsync 的区别:

    whenComplete:是执行当前任务的线程继续执行 whenComplete 的任务。whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。
    

    方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 以Async 结尾可能会使用其他线程执行 (如果是使用相同的线程池,也可能会被同一个线程选中执行)。

    • whenComplete
      无论任务正常还是异常结束,都可以在此方法中进行后续处理。其回调中可以同时获得任务的结果和异常信息:

      future.whenComplete((result, exception) -> {if (exception != null) {// 异常处理逻辑} else {// 正常处理逻辑}
      });
      
    • exceptionally
      用于捕获任务执行过程中出现的异常,并提供一个默认返回值:

      CompletableFuture<String> futureWithFallback = future.exceptionally(e -> "默认结果");
      

4. handle 最终处理

handle 方法是一个综合性的处理方式,可以同时处理正常结果与异常情况,其回调接收两个参数:上一步的结果和异常对象。你可以在 handle 中根据情况返回一个新的值,用于后续处理。

CompletableFuture<String> handledFuture = future.handle((result, exception) -> {if (exception != null) {// 出现异常时返回默认值return "默认结果";}// 正常时返回经过处理的结果,比如转换为大写return result.toUpperCase();
});

whenComplete 不同的是,handle 的返回值可以作为后续任务的输入,从而实现统一的结果处理。

image-20250328184727002


5. 线程串行化

线程串行化是指多个任务按照一定顺序依次执行,前一个任务的输出作为下一个任务的输入。这种方式常见于需要依赖前一个步骤结果的场景:

  • thenApply
    用于对上一步结果进行转换:当一个线程依赖另一个线程是,获取上一个人物返回的结果,并返回当前任务的返回值

    CompletableFuture<String> futureChain = CompletableFuture.supplyAsync(() -> "初始结果").thenApply(result -> result + " -> 处理后结果");
    
  • thenAccept
    消费处理结果。接受任务的处理结果,并消费处理,无返回结果。

    CompletableFuture<String> futureChain = CompletableFuture.supplyAsync(() -> "初始结果").thenAccept();
    
  • thenRun

    只要上面的任务执行完成,就开始执行thenRun,只是处理完任务之后,执行thenRun的后续操作

 public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {System.out.println("当前线程:" + Thread.currentThread().getName());int i = 10 / 2;System.out.println("运行结果...." + i);return i;}, executor).thenApplyAsync(res -> {System.out.println("任务二启动了..." + "拿到了上一步的结果:" + res);return res*2;}, executor);Integer integer = future.get();System.out.println("返回数据:"+integer);}

这种链式调用的方式,确保了任务间严格的顺序执行和数据传递,使得编写复杂的业务逻辑更加直观和易于维护。


6. 线程任务组合

两任务组合-都要完成
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor);public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor);public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action);public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action);public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor);

两个任务必须都完成,触发该任务。

thenCombine:组合两个future,获取两个future的返回结果,并返回当前任务的返回值

thenAcceptBoth:组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值。

runAfterBoth:组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理该任务。

两任务组合-一个完成
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn);public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn);public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor);public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action);public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action);public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor);public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action);public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor);

当两个任务中,任意一个future任务完成的时候,执行任务。

applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。

acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。

runAfterEither:两个任务有一个执行完成,不需要获取future的结果,处理任务,也没有返回值。

多任务组合
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);

allOf:等待所有任务完成

anyOf:只要有一个任务完成

示例代码
package com.fancy.gulimall.search.thread;import java.util.concurrent.*;public class ThreadTest {public static ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {System.out.println("main....start....");
//      CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//          System.out.println("当前线程:" + Thread.currentThread().getId());
//          int i = 10 / 2;
//          System.out.println("运行结果:" + i);
//      }, executor);/*** 方法完成后的感知*          */
//       CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程:" + Thread.currentThread().getId());
//            int i = 10 / 0;
//            System.out.println("运行结果:" + i);
//            return i;
//       }, executor).whenComplete((res,excption)->{
//           //虽然能得到异常信息,但是没法修改返回数据。
//           System.out.println("异步任务成功完成了...结果是:"+res+";异常是:"+excption);
//       }).exceptionally(throwable -> {
//           //可以感知异常,同时返回默认值
//           return 10;
//       });/*** 方法执行完成后的处理*/
//       CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//           System.out.println("当前线程:" + Thread.currentThread().getId());
//           int i = 10 / 4;
//           System.out.println("运行结果:" + i);
//           return i;
//       }, executor).handle((res, thr) -> {
//           if (res != null) {
//               return res * 2;
//           }
//           if (thr != null) {
//               return 0;
//           }
//            return 0;
//       });//R apply(T t, U u);/*** 线程串行化* 1)、thenRun:不能获取到上一步的执行结果,无返回值*  .thenRunAsync(() -> {*             System.out.println("任务2启动了...");*         }, executor);* 2)、thenAcceptAsync;能接受上一步结果,但是无返回值* 3)、thenApplyAsync:;能接受上一步结果,有返回值*/
//       CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//           System.out.println("当前线程:" + Thread.currentThread().getId());
//           int i = 10 / 4;
//           System.out.println("运行结果:" + i);
//           return i;
//       }, executor).thenApplyAsync(res -> {
//           System.out.println("任务2启动了..." + res);
//
//           return "Hello " + res;
//       }, executor);//void accept(T t);//R apply(T t);//future.get()/*** 两个都完成*/
//       CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
//           System.out.println("任务1线程:" + Thread.currentThread().getId());
//           int i = 10 / 4;
//           System.out.println("任务1结束:" );
//           return i;
//       }, executor);
//
//       CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
//           System.out.println("任务2线程:" + Thread.currentThread().getId());
//
//           try {
//               Thread.sleep(3000);
//               System.out.println("任务2结束:" );
//           } catch (InterruptedException e) {
//               e.printStackTrace();
//           }
//           return "Hello";
//        }, executor);//     future01.runAfterBothAsync(future02,()->{
//         System.out.println("任务3开始...");
//     }, executor);// void accept(T t, U u);
//     future01.thenAcceptBothAsync(future02,(f1,f2)->{
//         System.out.println("任务3开始...之前的结果:"+f1+"--》"+f2);
//     }, executor);//R apply(T t, U u);
//     CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> {
//         return f1 + ":" + f2 + " -> Haha";
//     }, executor);/*** 两个任务,只要有一个完成,我们就执行任务3* runAfterEitherAsync:不感知结果,自己没有返回值* acceptEitherAsync:感知结果,自己没有返回值* applyToEitherAsync:感知结果,自己有返回值*/
//        future01.runAfterEitherAsync(future02,()->{
//            System.out.println("任务3开始...之前的结果:");
//        },executor);//void accept(T t);
//        future01.acceptEitherAsync(future02,(res)->{
//            System.out.println("任务3开始...之前的结果:"+res);
//        },executor);
//        CompletableFuture<String> future = future01.applyToEitherAsync(future02, res -> {
//            System.out.println("任务3开始...之前的结果:" + res);
//            return res.toString() + "->哈哈";
//        }, executor);CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {System.out.println("查询商品的图片信息");return "hello.jpg";},executor);CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {System.out.println("查询商品的属性");return "黑色+256G";},executor);CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(3000);System.out.println("查询商品介绍");} catch (InterruptedException e) {e.printStackTrace();}return "华为";},executor);//      CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);anyOf.get();//等待所有结果完成//      System.out.println("main....end...."+futureImg.get()+"=>"+futureAttr.get()+"=>"+futureDesc.get());System.out.println("main....end...."+anyOf.get());}
}

谷粒商城业务

业务描述

这个功能主要是满足,在商品详情页面查询时,通过一个接口去获取商品的有关的所有信息

sku基本信息
sku图片信息
获取spu销售属性组合
获取spu介绍
获取spu规格参数信息

其中有些查询是可以同时进行的,有些操作则需要在其他步骤返回结果后,拿到结果去继续查询,最后返回结果。
所以我们为了优化接口的加载速度可以选择异步编排

代码

线程池配置属性类

package com.atguigu.gulimall.product.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {private Integer coreSize;private Integer maxSize;private Integer keepAliveTime;
}

线程池配置类

package com.atguigu.gulimall.product.config;import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {@Beanpublic ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties threadPoolConfigProperties) {return new ThreadPoolExecutor(threadPoolConfigProperties.getCoreSize(), threadPoolConfigProperties.getMaxSize(), threadPoolConfigProperties.getKeepAliveTime(), TimeUnit.SECONDS, new LinkedBlockingDeque<>(10000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());}
}

主业务逻辑方法

  @Overridepublic SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {SkuItemVo skuItemVo = new SkuItemVo();CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {// sku基本信息SkuInfoEntity info = getById(skuId);skuItemVo.setInfo(info);return info;}, executor);CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {// spu销售属性组合List<SkuItemSaleAttrVo> saleAttrsBySpuId = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());skuItemVo.setSaleAttr(saleAttrsBySpuId);}, executor);CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync(res -> {// spu介绍SpuInfoDescEntity descEntity = spuInfoDescService.getById(res.getSpuId());skuItemVo.setDesp(descEntity);}, executor);CompletableFuture<Void> baseFuture = infoFuture.thenAcceptAsync((res) -> {// spu的规格参数List<SpuItemAttrGroupVo> groupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());skuItemVo.setGroupAttrs(groupVos);}, executor);CompletableFuture<Void> imagesFuture = CompletableFuture.runAsync(() -> {// sku图片信息List<SkuImagesEntity> skuImages = skuImagesService.getImagesBySkuId(skuId);skuItemVo.setImages(skuImages);}, executor);// 等待所有任务都完成CompletableFuture.allOf(infoFuture,saleAttrFuture,descFuture,baseFuture,imagesFuture).get();return skuItemVo;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/43113.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络运维学习笔记(DeepSeek优化版) 024 HCIP-Datacom OSPF域内路由计算

文章目录 OSPF域内路由计算&#xff1a;单区域的路由计算一、OSPF单区域路由计算原理二、1类LSA详解2.1 1类LSA的作用与结构2.2 1类LSA的四种链路类型 三、OSPF路由表生成验证3.1 查看LSDB3.2 查看OSPF路由表3.3 查看全局路由表 四、2类LSA详解4.1 2类LSA的作用与生成条件4.2 2…

飞桨PP系列新成员PP-DocLayout开源,版面检测加速大模型数据构建,超百页文档图像一秒搞定

背景介绍 文档版面区域检测技术通过精准识别并定位文档中的标题、文本块、表格等元素及其空间布局关系&#xff0c;为后续文本分析构建结构化上下文&#xff0c;是文档图像智能处理流程的核心前置环节。随着大语言模型、文档多模态及RAG&#xff08;检索增强生成&#xff09;等…

以科技赋能,炫我云渲染受邀参加中关村文化科技融合影视精品创作研讨会!

在文化与科技深度融合的时代浪潮下&#xff0c;影视创作行业经历着前所未有的变革。影视创作行业发展态势迅猛&#xff0c; 同时也面临着诸多挑战。为促进影视创作行业的创新发展&#xff0c;加强业内交流与合作&#xff0c; 3月25日下午&#xff0c;海淀区文化创意产业协会举办…

NFS挂载异常排查记录

互相PING服务器看是否通&#xff1b;在ubuntu下看下服务器是否正常运行。导出目录是否导出了。最后发现在挂载目录的地方目录路径和后面没有加空格。

flutter 专题 七十一 Flutter 自定义单选控件

在Flutter 应用开发中&#xff0c;经常会遇到各种单选效果&#xff0c;虽然官方提供了Radio组件&#xff0c;但是并不能满足我们实际的开发需求&#xff0c;所以往往还需要自定义控件才能满足平时的开发需求。下面就平时开发中用到的单选进行介绍&#xff1a; 自定义SegmentBa…

在Cesium中使用ThreeJs材质(不是场景融合哦)

在Cesium中使用ThreeJs材质(不是场景融合哦&#xff09;_哔哩哔哩_bilibili

浅谈Thread类及常见方法与线程的状态(多线程编程篇2)

目录 前言 1.Thread类及常见方法 Thread类中常见的属性 1. getId() 2. getName() 3. getState() 4. getPriority() 5. isDaemon() 6. isAlive() 7. isInterrupted() 2.Thread类中常见的方法 Thread.interrupt() (中断线程) Thread.start()(启动线程) 1. 覆写 run…

Elasticsearch:人工智能时代的公共部门数据治理

作者&#xff1a;来自 Elastic Darren Meiss 人工智能&#xff08;AI&#xff09;和生成式人工智能&#xff08;GenAI&#xff09;正在迅速改变公共部门&#xff0c;从理论探讨走向实际应用。正确的数据准备、管理和治理将在 GenAI 的成功实施中发挥关键作用。 我们最近举办了…

使用事件监听器来处理并发环境中RabbitMQ的同步响应问题

RabbitListener 是 Spring AMQP 提供的核心注解&#xff0c;用于简化 RabbitMQ 消息监听器的创建。以下是对 RabbitListener(queues "balloonWords.queue") 的详细解析&#xff1a; 一、基础功能 队列监听 通过 queues 属性指定监听的队列名称&#xff08;如 "…

2025年数智化电商产业带发展研究报告260+份汇总解读|附PDF下载

原文链接&#xff1a;https://tecdat.cn/?p41286 在数字技术与实体经济深度融合的当下&#xff0c;数智化产业带正成为经济发展的关键引擎。 从云南鲜花产业带的直播热销到深圳3C数码的智能转型&#xff0c;数智化正重塑产业格局。2023年数字经济规模突破53.9万亿元&#xff…

自动驾驶04:点云预处理03

点云组帧 感知算法人员在完成点云的运动畸变补偿后&#xff0c;会发现一个问题&#xff1a;激光雷达发送的点云数据包中的点云数量其实非常少&#xff0c;完全无法用来进行后续感知和定位层面的处理工作。 此时&#xff0c;感知算法人员就需要对这些数据包进行点云组帧的处理…

Servlet注解与使用模板方法设计模式优化oa项目

一、Servlet注解&#xff0c;简化配置 分析oa项目中的web.xml文件 现在只是一个单标的CRUD&#xff0c;没有复杂的业务逻辑&#xff0c;很简单的一丢丢功能。web.xml文件中就有如此多的配置信息。如果采用这种方式&#xff0c;对于一个大的项目来说&#xff0c;这样的话web.xml…

污水处理厂人员定位方案-UWB免布线高精度定位

1. 方案概述 本方案采用免布线UWB基站与北斗卫星定位融合技术&#xff0c;结合UWBGNSS双模定位工卡&#xff0c;实现污水处理厂室内外人员高精度定位&#xff08;亚米级&#xff09;。系统通过低功耗4G传输数据&#xff0c;支持实时位置监控、电子围栏、聚集预警、轨迹回放等功…

【C++初阶】第12课—list

文章目录 1. list的构造2. list迭代器的常见接口2.1 list遍历的迭代器接口2.2 list修改数据的迭代器接口2.3 list排序、逆序、合并相关操作的成员函数 3. 模拟实现list3.1 模拟实现list的构造3.2 模拟实现list的尾插3.3 模拟实现迭代器iterator3.4 模拟实现list的插入删除3.5 模…

Java进阶

Java进阶 注解什么是注解&#xff1f;内置注解元注解自定义注解 对象克隆&#xff08;对象复制&#xff09;如何实现克隆&#xff1f;浅克隆深克隆 设计模式统一建模语言&#xff08;UML&#xff09;类接口类之间的关系 面向对象设计原则1. 单一职责2. 开闭原则3. 里氏替换原则…

AB包介绍及导出工具实现+AB包资源简单加载

Resource原理 项目中建立Resources目录&#xff0c;资源导入内部 生成项目包 资源文件存储路径 结论&#xff1a;存储在Resources下的资源&#xff0c;最终会存储在游戏的主体包中&#xff0c;发送给用户&#xff0c;手机系统上&#xff0c;如果需要做资源的更新&#xff0c;是…

全包圆玛奇朵样板间亮相,极简咖啡风引领家装新潮流

在追求品质生活的当下&#xff0c;家居装修风格的选择成为了许多消费者关注的焦点。近日&#xff0c;全包圆家居装饰有限公司精心打造的玛奇朵样板间正式对外开放&#xff0c;以其独特的咖啡色系极简风格&#xff0c;为家装市场带来了一股清新的潮流。玛奇朵样板间不仅展示了全…

算法基础——模拟

目录 1 多项式输出 2.蛇形方阵 3.字符串的展开 模拟&#xff0c;顾名思义&#xff0c;就是题⽬让你做什么你就做什么&#xff0c;考察的是将思路转化成代码的代码能⼒。这类题⼀般较为简单&#xff0c;属于竞赛⾥⾯的签到题&#xff08;但是&#xff0c;万事⽆绝对&#xff…

Java---类与对象

类与对象 前言&#xff1a;一、面向对象二、类的定义1.类的定义格式2.访问修饰限定符 三、类的实例化四、this引用1.this引用2.this引用的原因 五、对象的构造和初始化1.初始化对象2.构造方法(1).构造方法的概念&#xff1a;(2).特性&#xff1a;(3).this调用:3.就地初始化4.默…

【巧文书高效编标工具】技术解决方案

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 巧文书高效编标工具**是一款基于自然语言处理&#xff08;NLP&#xff09;与自动化技术的智能编标解决方案&#xff0c;旨在提升标书编制效率、降低人工错误率。通过结合规则引擎、模板化生成和AI辅助校对&#xff0c…