java建立執行緒池的幾中方式

晓海无涯苦揍舟發表於2024-07-18

1.建立執行緒池四種方式

  1. 使用 Executors 類Executors 類是 Java 中用於建立執行緒池的工廠類,它提供了多種靜態方法來建立不同型別的執行緒池

  2. 使用 ThreadPoolExecutor 類,ThreadPoolExecutor 是 Java 中執行緒池的一個核心類,它提供了更細粒度的控制來建立和管理執行緒池

  3. 使用 Future 和 Callable,Future 和 Callable 是併發程式設計中非常重要的兩個介面,它們通常與 ExecutorService 一起使用來執行非同步任務。

  4. 使用 spring 的 ThreadPooltaskExecutor,ThreadPoolTaskExecutor 是一個基於 java.util.concurrent.ThreadPoolExecutor 的擴充套件,提供了更豐富的配置選項和與Spring整合的特性

2.執行緒池重要引數

  1. corePoolSize (int): 執行緒池的基本大小,即在沒有任務執行時執行緒池的大小。當新任務提交時,執行緒池會優先使用已有的空閒執行緒。

  2. maximumPoolSize (int): 執行緒池能夠容納同時執行的最大執行緒數。這個引數用於控制執行緒池的最大規模,防止因任務過多而導致資源耗盡。

  3. keepAliveTime (long): 當執行緒池中的執行緒數量超過 corePoolSize 時,多餘的空閒執行緒能等待新任務的最長時間。超過這個時間後,多餘的執行緒將被終止。

  4. unit (TimeUnit): keepAliveTime 引數的時間單位,常見的時間單位有 TimeUnit.SECONDSTimeUnit.MINUTES 等。

  5. workQueue (BlockingQueue<Runnable>): 一個阻塞佇列,用於儲存等待執行的任務。常用的阻塞佇列有 LinkedBlockingQueueArrayBlockingQueueSynchronousQueue 等。

  6. threadFactory (ThreadFactory): 用於建立新執行緒的工廠。可以透過實現 ThreadFactory 介面來自定義執行緒的建立過程。

  7. handler (RejectedExecutionHandler): 當任務太多而執行緒池無法處理時,用於定義拒絕任務的策略。常見的拒絕策略有 ThreadPoolExecutor.AbortPolicyThreadPoolExecutor.CallerRunsPolicyThreadPoolExecutor.DiscardPolicy 等。

package com.demo.threadPool;

import java.util.concurrent.*;

public class MainDemo1 {
    public static void main(String[] args) {
        int corePoolSize = 5; // 核心執行緒數
        int maximumPoolSize = 10; // 最大執行緒數
        long keepAliveTime = 1; // 非核心執行緒空閒存活時間
        /**
         * 存活時間單位
         * TimeUnit.DAYS:天
         * TimeUnit.HOURS:小時
         * TimeUnit.MINUTES:分
         * TimeUnit.SECONDS:秒
         * TimeUnit.MILLISECONDS:毫秒
         * TimeUnit.MICROSECONDS:微妙
         * TimeUnit.NANOSECONDS:納秒
         */
        TimeUnit unit = TimeUnit.MINUTES;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(); // 工作佇列
        ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 執行緒工廠
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒絕策略
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
    }
}

3.執行緒池5種狀態

  1. RUNNING:正常執行狀態,可接收新任務,可處理阻塞佇列中的任務
  2. SHUTDOWN:不會接收新任務,但會處理阻塞佇列剩餘任務
  3. STOP:會中斷正在執行的任務,並拋棄阻塞佇列任務
  4. TIDYING:任務全執行完畢,活動執行緒為 0,即將進入終結
  5. TERMINATED:終結狀態

4.Executors 類建立執行緒池

  1. new newCachedThreadPool(): 建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。執行緒池的規模不存在限制。(數量不固定的執行緒池)
  2. new newFixedThreadPool() : 建立一個固定長度執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。(固定數量的執行緒池)
  3. new newScheduledThreadPool() : 建立一個固定長度執行緒池,支援定時及週期性任務執行。(定時執行緒池)
  4. new newSingleThreadExecutor() : 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。(單執行緒的執行緒池)
  • 固定執行緒池 ( Executors.newFixedThreadPool(5) ):建立一個固定大小的執行緒池。執行緒池中的執行緒數量是固定的,即使有些執行緒處於空閒狀態,它們也不會被回收。
package com.demo.threadPool;

import java.util.List;
import java.util.concurrent.*;

public class MainThreadPool {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
//初始化固定大小執行緒池 ExecutorService executor1 = Executors.newFixedThreadPool(5);
//使用 execute(Runnable command) 方法提交一個不需要返回結果的任務, // 或者使用submit(Callable<T> task) 方法提交一個需要返回結果的任務。 for (int i = 0; i < 10; i++) { executor1.execute(new TaskR(i)); } //使用 submit(Callable<T> task) 任務並獲取 Future //使用 Future.get() 方法等待任務完成並獲取結果。這個方法會阻塞呼叫執行緒直到任務完成。 for (int i = 0; i < 10; i++) { Future<String> future = executor1.submit(new TaskC(i)); System.out.println("執行緒返回結果 "+future.get()); } // 當所有任務都執行完畢,或者需要關閉執行緒池時,呼叫 shutdown() 方法。 // 這將等待正在執行的任務完成,但不接收新任務。 executor1.shutdown(); //使用 shutdownNow() 方法嘗試立即停止所有正在執行的任務,並返回等待執行的任務列表 List<Runnable> notExecutedTasks = executor1.shutdownNow(); for(Runnable ls : notExecutedTasks){ System.out.println(ls); } //使用 awaitTermination() 方法等待執行緒池關閉,直到所有任務完成或超時。 boolean res = executor1.awaitTermination(60, TimeUnit.SECONDS); System.out.println("執行結果:"+res); } } /** * 實現 Runnable 介面 */ class TaskR implements Runnable { private int id; public TaskR(int id) { this.id = id; } public void run() { System.out.println("TaskR " + id + " is running..."); } } /** * 實現 Callable 介面 * 有返回值 */ class TaskC implements Callable { private int id; public TaskC(int id) { this.id = id; } @Override public Object call(){ System.out.println("TaskC " + id + " is running..."); return id+"--TaskC"; } }
  • 單執行緒池 (newSingleThreadExecutor):建立一個只有一個執行緒的執行緒池。即使有多個任務提交,它們也會被排隊,逐個由單個執行緒執行
package com.demo.threadPool;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 單執行緒池 (newSingleThreadExecutor):
 * 建立一個只有一個執行緒的執行緒池。即使有多個任務提交,它們也會被排隊,逐個由單個執行緒執行。
 */
public class MainOne {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        /**
         * 單執行緒:建立的執行服務內部有一個執行緒。所有提交給它的任務將會序列化執行,也就是說,它會在單個執行緒上依次執行任務,不會有併發執行的情況發生
         * 任務佇列:如果有多個任務提交給這個執行器,除了當前正在執行的任務外,其他任務將會在一個無界佇列中等待,直到執行緒可用
         * 處理任務失敗:如果執行中的執行緒由於任務丟擲異常而終止,執行服務會安排一個新的執行緒來替換它,以繼續執行後續的任務
         * 使用場景: newSingleThreadExecutor 非常適合需要順序執行的任務,並且要求任務之間不受併發問題影響的場景
         */
        ExecutorService executor = Executors.newSingleThreadExecutor();

        for (int i = 0; i < 10; i++) {
            executor.execute(new TaskR(i));
        }

        //使用 submit(Callable<T> task) 任務並獲取 Future
        //使用 Future.get() 方法等待任務完成並獲取結果。這個方法會阻塞呼叫執行緒直到任務完成。
        for (int i = 0; i < 10; i++) {
            Future<String> future =  executor.submit(new TaskC(i));
            System.out.println("執行緒返回結果  "+future.get());
        }
        // 當所有任務都執行完畢,或者需要關閉執行緒池時,呼叫 shutdown() 方法。
        // 這將等待正在執行的任務完成,但不接收新任務。
        executor.shutdown();
    }
}
  • 快取執行緒池 (newCachedThreadPool):建立一個可根據需要建立新執行緒的執行緒池。如果執行緒空閒超過60秒,它們將被終止並從池中移除
package com.demo.threadPool;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 快取執行緒池 (newCachedThreadPool):
 * 建立一個可根據需要建立新執行緒的執行緒池。如果執行緒空閒超過60秒,它們將被終止並從池中移除
 */
public class MainCacheThreadPool {
    public static void main(String[] args) throws InterruptedException {

        System.out.println(Thread.currentThread().getName() + "執行緒: Start at: " + new Date());
        //初始化快取執行緒池
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 1; i < 10; i++) {
            System.out.println("新增了第" + i + "個任務類");
            Thread.sleep(2000);
            exec.execute(new TaskR(i));
        }
        //所有任務結束後關閉執行緒池
        exec.shutdown();
        System.out.println(Thread.currentThread().getName() + " 執行緒: Finished all threads at:" + new Date());

    }
}
  • 排程執行緒池 (newScheduledThreadPool):建立一個支援定時任務和週期性任務的執行緒池
package com.demo.threadPool;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 固定頻率執行
 * 排程執行緒池 (newScheduledThreadPool):
 * 建立一個支援定時任務和週期性任務的執行緒池
 */
public class MainScheduledThreadPool {
    public static void main(String[] args) {
        /**
         * 場景描述
         * 假設你需要一個應用程式,該程式能夠每10秒執行一次任務,並在啟動後1分鐘開始執行。此外,
         * 你還需要能夠安排一次性任務在未來的某個時間點執行
         */
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);

        // 安排定期任務
        // 初始延遲1分鐘,之後每10秒執行一次
        threadPool.scheduleAtFixedRate(new TaskR(2), 60, 10, TimeUnit.SECONDS);

        // 安排一次性任務
        // 使用 schedule 方法安排一個任務,在指定的延遲後執行一次
        // 延遲5分鐘後執行
        threadPool.schedule(new TaskR(3), 5, TimeUnit.MINUTES);

        // 關閉執行緒池
        // 當不再需要執行緒池時,呼叫 shutdown 方法來關閉執行緒池。這將等待正在執行的任務完成,但不接收新任務
        threadPool.shutdown();

        // 等待執行緒池關閉
        // 使用 awaitTermination 方法等待執行緒池關閉,直到所有任務完成或超時。
        try {
            threadPool.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
  • 使用給定的執行緒工廠建立執行緒池:可以提供一個自定義的 ThreadFactory 來建立執行緒池中的執行緒
package com.demo.threadPool;


import java.util.concurrent.*;

/**
 * 使用給定的執行緒工廠建立執行緒池
 */
public class MainFactory {
    public static void main(String[] args) {
        //自定義執行緒工廠建立
        ThreadFactory threadFactory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r);
            }
        };
        //使用給定的執行緒工廠建立執行緒池
        ExecutorService executor = Executors.newFixedThreadPool(5, threadFactory);
        executor.execute(new TaskR(2));
    }
}
  • 自定義執行緒工廠建立:自定義執行緒工廠可以設定自己的執行緒名,設定守護執行緒,設定執行緒優先順序,處理未捕獲的異常等
package com.demo.threadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
 *  自定義執行緒工廠:設定執行緒名,守護執行緒,優先順序以及UncaughtExceptionHandler
 */
public class MainFactory implements ThreadFactory {

    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    public MainFactory(String namePrefix) {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        this.namePrefix = namePrefix + "-thread-";
    }

    public MainFactory(ThreadGroup group, String namePrefix) {
        this.group = group;
        this.namePrefix = namePrefix;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);
        //守護執行緒
        if (t.isDaemon())
            t.setDaemon(true);
        //執行緒優先順序
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        /**
         * 處理未捕捉的異常
         */
        t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("處理未捕獲的異常");
            }
        });
        return t;
    }

    //測試方法
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(5, new MainFactory("測試執行緒"));
        for (int i = 0; i < 10; i++) {
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("執行緒處理");
                    //未捕獲的異常,走自定義的UncaughtExceptionHandler邏輯
                    int i = 1 / 0;
                }
            });
        }
        pool.shutdown();
    }
}

5.ThreadPoolExecutor 類建立執行緒池

  ThreadPoolExecutorjava.util.concurrent 包中用來建立執行緒池的一個類。它提供了一種靈活的方式來管理執行緒池,允許你控制執行緒的建立和銷燬。

  ThreadPoolExecutor 類中的幾個重要方法

  1. execute():向執行緒池提交一個任務,交由執行緒池去執行
  2. submit():也是向執行緒池提交任務,但是和execute()方法不同,它能夠返回任務執行的結果它實際上還是呼叫的 execute() 方法,只不過它利用了 Future 來獲取任務執行結果
  3. invokeAll():提交一個任務集合
  4. invokeAny(): 提交一個任務集合,哪個任務先成功執行完畢,返回此任務執行結果,其它任務取消
  5. shutdown():關閉執行緒池,再也不會接受新的任務不會立即終止執行緒池,而是要等所有任務快取佇列中的任務都執行完後才終止
  6. shutdownNow():關閉執行緒池,再也不會接受新的任務立即終止執行緒池,並嘗試打斷正在執行的任務,並且清空任務快取佇列,返回尚未執行的任務
  7. isShutdown():不在 RUNNING 狀態的執行緒池,此方法就返回 true
  8. isTerminated():執行緒池狀態是否是 TERMINATED
package com.demo.threadPool;
import java.util.Random;
import java.util.concurrent.*;
/**
 * ThreadPoolExecutor 是 java.util.concurrent 包中用來建立執行緒池的一個類
 * 它提供了一種靈活的方式來管理執行緒池,允許你控制執行緒的建立和銷燬。
 * 以下是幾種常見的建立 ThreadPoolExecutor 執行緒池的方式
 * 實際上 Executors 類也是呼叫 ThreadPoolExecutor 類建立的執行緒
 */
public class MainThreadPoolExecutor {
    //測試方法
    public static void main(String[] args) {
        /**
         * 核心執行緒數,核心執行緒就是一直存在的執行緒
         */
        int corePoolSize = 5;
        /**
         * 最大執行緒數,表示執行緒池中最多能建立多少個執行緒
         * 非核心執行緒數 = 最大執行緒數 - 核心執行緒數
         */
        int maximumPoolSize = 10;
        /**
         * 預設情況下,只有當執行緒池中的執行緒數大於corePoolSize時,
         * keepAliveTime才會起作用,則會終止,直到執行緒池中的執行緒數不超過corePoolSize
         * 則會終止,直到執行緒池中的執行緒數不超過corePoolSize
         * 但是如果呼叫了 allowCoreThreadTimeOut(boolean) 方法
         * 線上程池中的執行緒數不大於corePoolSize時,keepAliveTime引數也會起作用,直到執行緒池中的執行緒數為 0
         * 針對非核心執行緒而言,表示執行緒沒有任務執行時最多保持多久時間會終止
         */
        long keepAliveTime = 60;
        /**
         * 時間單位
         * 與 keepAliveTime 配合使用,針對非核心執行緒
         */
        TimeUnit unit = TimeUnit.SECONDS;
        /**
         * 存放任務的阻塞佇列
         */
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(5);
        /**
         * 建立執行緒的工廠,可以為執行緒建立時起個好名字
         */
        ThreadFactory threadFactory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r);
            }
        };
        /**
         * 拒絕策略
         * 任務太多的時候會進行拒絕操作
         * 核心執行緒,非核心執行緒,任務佇列都放不下時
         */
        // 自定義拒絕策略
        RejectedExecutionHandler defaultHandler1 = new MyRejectedExecutionHandler();
        // 預設策略,在需要拒絕任務時丟擲RejectedExecutionException
        RejectedExecutionHandler defaultHandler3 = new ThreadPoolExecutor.AbortPolicy();
        // 直接在 execute 方法的呼叫執行緒中執行被拒絕的任務,如果執行緒池已經關閉,任務將被丟棄;
        RejectedExecutionHandler defaultHandler2 = new ThreadPoolExecutor.CallerRunsPolicy();
        // 直接丟棄任務
        RejectedExecutionHandler defaultHandler4 = new ThreadPoolExecutor.DiscardPolicy();
        // 丟棄佇列中等待時間最長的任務,並執行當前提交的任務,如果執行緒池已經關閉,任務將被丟棄
        RejectedExecutionHandler defaultHandler5 = new ThreadPoolExecutor.DiscardOldestPolicy();
        /**
         * 建立執行緒池
         */
        ExecutorService service1 =  new ThreadPoolExecutor( corePoolSize, maximumPoolSize,keepAliveTime, 
                unit,workQueue,threadFactory,defaultHandler1);
        for (int i = 0; i < 10; i++) {
            System.out.println("新增第"+i+"個任務");
            service1.execute(new MyThread("執行緒"+i));
        }
        service1.shutdown();
    }
}

/**
 * 自定義拒絕策略
 */
class MyRejectedExecutionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        new Thread(r,"新執行緒"+new Random().nextInt(10)).start();
    }
}

/**
 * 執行緒類
 */
class MyThread implements Runnable {
    String name;
    public MyThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("執行緒:"+Thread.currentThread().getName() +" 執行:"+name +"  run");
    }
}

6.Future 和 Callable 類使用建立執行緒池

Callable 是一個函式式介面,它允許你定義一個任務,該任務可以返回一個結果並丟擲異常。它是 Runnable 介面的擴充套件,增加了返回值和丟擲異常的能力。

  • 返回值:與 Runnable 介面不同,Callable 任務可以返回一個值,返回值透過 Future 物件獲取。
  • 異常Callable 任務可以丟擲異常,這些異常可以透過 Future 物件處理。

Future 介面代表非同步計算的結果。它提供了檢查計算是否完成的方法,以及獲取計算結果的方法。

  • get():獲取計算結果。如果計算尚未完成,此方法會阻塞,直到計算完成或丟擲異常。
  • isDone():檢查計算是否完成。
  • cancel():嘗試取消任務。
  • isCancelled():檢查任務是否被取消。
package com.demo.threadPool;

import java.util.concurrent.*;

/**
 * Future 使用
 */
public class MainFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        System.out.println("開始時間戳為:" + System.currentTimeMillis());
        Future<String> future = executorService.submit(new Test01());
        String result = future.get(); //獲取計算結果。如果計算尚未完成,此方法會阻塞,直到計算完成或丟擲異常
        boolean isdone = future.isDone();  //檢查計算是否完成
        boolean cancel = future.cancel(true);  //嘗試取消任務
        boolean iscancelled = future.isCancelled(); //檢查任務是否被取消
        System.out.println("result:"+result);
        System.out.println("isdone:"+isdone);
        System.out.println("cancel:"+cancel);
        System.out.println("iscancelled:"+iscancelled);
        System.out.println("結束時間戳為:" + System.currentTimeMillis());
     executorService.shutdown(); } }
class Test01 implements Callable { @Override public Object call() throws Exception { return "你好"; } }

7.Spring 的 ThreadPoolTaskExecutor 類建立執行緒池

ThreadPoolTaskExecutor 是 Spring 框架提供的一個執行緒池實現,它擴充套件了 Java 的 ThreadPoolExecutor 並提供了一些額外的配置和功能

  1. 新增依賴: 如果你的專案是一個 Maven 專案,確保你的 pom.xml 檔案中包含了 Spring Boot 的依賴
  2. 配置執行緒池: 在 Spring Boot 應用程式中,你可以透過 Java 配置類來配置 ThreadPoolTaskExecutor
package com.cnpc.epai.assetcatalog.dmp.controller;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
 * 執行緒池配置類
 */
@Configuration
public class ConfigPoolConfiguration {
    @Bean("TaskExecutorDemo")
    public ThreadPoolTaskExecutor taskExecutorDemo(){
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(10); // 核心執行緒數
        threadPoolTaskExecutor.setMaxPoolSize(20);// 最大執行緒數
        threadPoolTaskExecutor.setQueueCapacity(100); //工作佇列
        threadPoolTaskExecutor.setKeepAliveSeconds(60); // 非核心執行緒的空閒存活時間
        threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);//指定是否允許核心執行緒超時。這允許動態增長和收縮,即使與非零佇列結合使用也是如此(因為最大池大小隻有在佇列已滿時才會增長)
        threadPoolTaskExecutor.setThreadNamePrefix("monitor-thread-pool-");// 設定執行緒名字首
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// 拒絕策略
        threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);// 設定執行緒池關閉時需要等待子任務執行完畢,才銷燬對應的bean
        threadPoolTaskExecutor.initialize();//初始化執行緒池
        return threadPoolTaskExecutor;
    }
}

測試類

package com.cnpc.epai.assetcatalog.dmp.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

@Service
public class TestService {
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Async("taskExecutor")
    public void executeTask() {
        taskExecutor.execute(() -> {
            System.out.println("Executing task in thread: " + Thread.currentThread().getName());
        });
    }
}

相關文章