谷粒微服務高級篇學習筆記整理---異步線程池

多線程回顧

多線程實現的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.pswp.cn/news/899461.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899461.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899461.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Windows學習筆記(4)關于MITRE

基本術語 APT&#xff08;威脅組&#xff0c;高級持續威脅&#xff09; TTP&#xff08;攻擊目的技術過程&#xff0c;戰術技術和程序&#xff09; ATT&CK框架 網站 https://attack.mitre.org/ CAR知識庫 MITRE Engage MITRE D3FEND 網址 https://d3fend.mitre.org/

Go 語言規范學習(2)

文章目錄 VariablesTypesBoolean typesNumeric typesString typesArray typesSlice typesStruct typesPointer typesFunction typesInterface typesBasic interfacesEmbedded interfacesGeneral interfaces【泛型接口】Implementing an interface【實現一個接口】 Map typesCha…

創意 Python 愛心代碼分享

創意 Python 愛心代碼分享 在編程中&#xff0c;用代碼表達創意和情感是一種非常有趣的方式。本文將分享幾段用 Python 編寫的愛心代碼&#xff0c;涵蓋簡單到復雜的實現方式&#xff0c;適合初學者和進階開發者。 1. 簡單愛心圖案 代碼實現 print("\n".join([&qu…

NLP高頻面試題(二十四)——RAG相關內容簡介

檢索增強生成&#xff08;Retrieval-Augmented Generation&#xff0c;簡稱 RAG&#xff09;是一種將信息檢索與生成模型相結合的技術&#xff0c;旨在提升大型語言模型的響應準確性、相關性和時效性。通過在生成過程中引入外部知識&#xff0c;RAG 能夠有效彌補 LLM 在知識局限…

Share01-WinCC文件越用越大?

為什么你們的經典WinCC項目在客戶電腦上運行的越來越慢&#xff1f;為什么查詢一個歷史曲線慢的要死&#xff1f;為什么重啟一下電腦畫面都要懷疑人生&#xff1f;具體原因可能多種多樣&#xff0c;但是極大可能是您的數據管理設置欠佳&#xff0c;那么閑話少敘&#xff0c;和小…

練習題:111

目錄 Python題目 題目 題目分析 需求理解 關鍵知識點 實現思路分析 代碼實現 代碼解釋 指定文件路徑和名稱&#xff1a; 定義要寫入的內容&#xff1a; 打開文件并寫入內容&#xff1a; 異常處理&#xff1a; 輸出提示信息&#xff1a; 運行思路 結束語 Python題…

2025_0327_生活記錄

昨晚正在玩手機&#xff0c;凌晨一點二十一分左右手機突然響起來&#xff0c;通知地震波將在5秒后到達海淀區。看著倒計時的數字不斷減小&#xff0c;橙色預警頁面不斷閃動&#xff0c;床猛地搖了幾下。那一刻&#xff0c;我的記憶被拉回了2008年。 上大學之前我在成都生活了1…

基于改進粒子群算法的多目標分布式電源選址定容規劃(附帶Matlab代碼)

通過分析分布式電源對配電網的影響&#xff0c;以有功功率損耗、電壓質量及分布式電源總容量為優化目標&#xff0c;基于模糊理論建立了分布式電源在配電網中選址定容的多目標優化模型&#xff0c;并提出了一種改進粒子群算法進行求解。在算例仿真中&#xff0c;基于IEEE-14標準…

雨云云應用測評!內測持續進行中!

大家好&#xff0c;時隔一個月&#xff0c;我們又見面了&#xff01; 最近&#xff0c;雨云推出了新型云應用&#xff08;RCA&#xff0c;Rainyun Cloud Application&#xff09;。 通過云應用&#xff0c;你可以快速創建可以外部訪問的應用&#xff0c;采用全新的面板和dock…

【研究方向】聯邦|自然語言

聯邦學習 Federated Learning,FL 分布式學習方案。 通過多個參與方&#xff08;client&#xff09; 聯邦計算 Federated Computing 聯邦計算(Federated Learning)是一種分布式 機器學習 方法,旨在解決數據隱私保護與數據孤島問題。 圖聯邦 Graph Neural Networks,GNNs 圖聯…

【算法day25】 最長有效括號——給你一個只包含 ‘(‘ 和 ‘)‘ 的字符串,找出最長有效(格式正確且連續)括號子串的長度。

32. 最長有效括號 給你一個只包含 ‘(’ 和 ‘)’ 的字符串&#xff0c;找出最長有效&#xff08;格式正確且連續&#xff09;括號子串的長度。 https://leetcode.cn/problems/longest-valid-parentheses/ 2.方法二&#xff1a;棧 class Solution { public:int longestValid…

C++編程學習筆記:函數相關特性、引用與編譯流程

目錄 一、函數的缺省參數 &#xff08;一&#xff09;全缺省參數 &#xff08;二&#xff09;半缺省參數 二、函數重載 &#xff08;一&#xff09;參數類型不同 &#xff08;二&#xff09;參數個數不同 &#xff08;三&#xff09;參數類型順序不同 三、引用相關問題…

RPCGC閱讀

24年的MM 創新 現有點云壓縮工作主要集中在保真度優化上。 而在實際應用中&#xff0c;壓縮的目的是促進機器分析。例如&#xff0c;在自動駕駛中&#xff0c;有損壓縮會顯著丟失戶外場景的詳細信息。在三維重建中&#xff0c;壓縮過程也會導致場景數據中語義信息(Contour)的…

泛目錄優化:無極泛目錄優化網站,技術解析與風險控制指南

無極泛目錄優化網站精簡版 一、核心功能 無限層級目錄&#xff1a;支持動態創建 5 級以上子目錄&#xff0c;形成內容矩陣AI 內容生成&#xff1a;集成 GPT-4 接口&#xff0c;日均生產 10 萬 原創度 70% 以上的頁面SEO 智能檢測&#xff1a;自動優化 TDK、URL 結構、圖片屬…

歸檔重做日志archived log (明顯) 比redo log重做日志文件小

歸檔重做日志 (明顯) 比重做日志文件小。 (文檔 ID 1356604.1) 日志切換將由于以下原因發生&#xff1a; 1. 由于在重做日志文件已滿之前強制創建存檔而記錄和設計的行為 SQL> alter system switch logfile;SQL> alter system archive log current;RMAN> backup ar…

645.錯誤的集合

import java.util.HashMap; import java.util.Map;/*** program: Test* description: 645 錯誤的集合* author: gyf* create: 2025-03-23 10:22**/ public class Test {public static void main(String[] args) {}public static int[] findErrorNums(int[] nums) {int[] arr n…

力扣刷題494. 目標和

494. 目標和 - 力扣&#xff08;LeetCode&#xff09; 方法一&#xff0c;暴力dfs 直接進行深搜查找出所有的情況&#xff0c;缺點嚴重超時&#xff0c;只能過20個案例 留一下超時的 class Solution {//首先定義全局變量int[] abs { 1, -1 }; //用來記錄當前遍歷的數的正…

一周學會Flask3 Python Web開發-SQLAlchemy數據遷移migrate

鋒哥原創的Flask3 Python Web開發 Flask3視頻教程&#xff1a; 2025版 Flask3 Python web開發 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili 模型類(表)不是一成不變的&#xff0c;當你添加了新的模型類&#xff0c;或是在模型類中添加了新的字段&#xff0c;甚至是修改…

Python練習之抽獎界面

前言 一、代碼整體架構分析 1、數據層 (Model) 2、控制層 (Controller) 3、視圖層 (View) 二、核心功能實現詳解 1、 文件導入功能 1.1、實現邏輯 1.2、代碼涉及知識點講解 1.2.1、wildcard 1.2.2、wx.FileDialog 1.2.3、dlg.ShowModal() 2、抽獎動畫控制 1.1、…

【云原生】docker 搭建單機PostgreSQL操作詳解

目錄 一、前言 二、前置準備 2.1 服務器環境 2.2 docker環境 三、docker安裝PostgreSQL過程 3.1 獲取PostgreSQL鏡像 3.2 啟動容器 3.2.1 創建數據卷目錄 3.2.2 啟動pg容器 3.3 客戶端測試連接數據庫 四、創建數據庫與授權 4.1 進入PG容器 4.2 PG常用操作命令 4.2…