1.建立執行緒池四種方式
-
使用 Executors 類
,
Executors
類是 Java 中用於建立執行緒池的工廠類,它提供了多種靜態方法來建立不同型別的執行緒池 -
使用 ThreadPoolExecutor 類,
ThreadPoolExecutor
是 Java 中執行緒池的一個核心類,它提供了更細粒度的控制來建立和管理執行緒池 -
使用 Future 和 Callable,Future 和 Callable 是併發程式設計中非常重要的兩個介面,它們通常與
ExecutorService
一起使用來執行非同步任務。 -
使用 spring 的 ThreadPooltaskExecutor,
ThreadPoolTaskExecutor
是一個基於java.util.concurrent.ThreadPoolExecutor
的擴充套件,提供了更豐富的配置選項和與Spring整合的特性
2.執行緒池重要引數
-
corePoolSize (
int
): 執行緒池的基本大小,即在沒有任務執行時執行緒池的大小。當新任務提交時,執行緒池會優先使用已有的空閒執行緒。 -
maximumPoolSize (
int
): 執行緒池能夠容納同時執行的最大執行緒數。這個引數用於控制執行緒池的最大規模,防止因任務過多而導致資源耗盡。 -
keepAliveTime (
long
): 當執行緒池中的執行緒數量超過corePoolSize
時,多餘的空閒執行緒能等待新任務的最長時間。超過這個時間後,多餘的執行緒將被終止。 -
unit (
TimeUnit
):keepAliveTime
引數的時間單位,常見的時間單位有TimeUnit.SECONDS
、TimeUnit.MINUTES
等。 -
workQueue (
BlockingQueue<Runnable>
): 一個阻塞佇列,用於儲存等待執行的任務。常用的阻塞佇列有LinkedBlockingQueue
、ArrayBlockingQueue
和SynchronousQueue
等。 -
threadFactory (
ThreadFactory
): 用於建立新執行緒的工廠。可以透過實現ThreadFactory
介面來自定義執行緒的建立過程。 -
handler (
RejectedExecutionHandler
): 當任務太多而執行緒池無法處理時,用於定義拒絕任務的策略。常見的拒絕策略有ThreadPoolExecutor.AbortPolicy
、ThreadPoolExecutor.CallerRunsPolicy
和ThreadPoolExecutor.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種狀態
- RUNNING:正常執行狀態,可接收新任務,可處理阻塞佇列中的任務
- SHUTDOWN:不會接收新任務,但會處理阻塞佇列剩餘任務
- STOP:會中斷正在執行的任務,並拋棄阻塞佇列任務
- TIDYING:任務全執行完畢,活動執行緒為 0,即將進入終結
- TERMINATED:終結狀態
4.Executors 類建立執行緒池
- new newCachedThreadPool(): 建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。執行緒池的規模不存在限制。(數量不固定的執行緒池)
- new newFixedThreadPool() : 建立一個固定長度執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。(固定數量的執行緒池)
- new newScheduledThreadPool() : 建立一個固定長度執行緒池,支援定時及週期性任務執行。(定時執行緒池)
- 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 類建立執行緒池
ThreadPoolExecutor
是 java.util.concurrent
包中用來建立執行緒池的一個類。它提供了一種靈活的方式來管理執行緒池,允許你控制執行緒的建立和銷燬。
ThreadPoolExecutor 類中的幾個重要方法
- execute():向執行緒池提交一個任務,交由執行緒池去執行
- submit():也是向執行緒池提交任務,但是和execute()方法不同,它能夠返回任務執行的結果它實際上還是呼叫的 execute() 方法,只不過它利用了 Future 來獲取任務執行結果
- invokeAll():提交一個任務集合
- invokeAny(): 提交一個任務集合,哪個任務先成功執行完畢,返回此任務執行結果,其它任務取消
- shutdown():關閉執行緒池,再也不會接受新的任務不會立即終止執行緒池,而是要等所有任務快取佇列中的任務都執行完後才終止
- shutdownNow():關閉執行緒池,再也不會接受新的任務立即終止執行緒池,並嘗試打斷正在執行的任務,並且清空任務快取佇列,返回尚未執行的任務
- isShutdown():不在 RUNNING 狀態的執行緒池,此方法就返回 true
- 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
並提供了一些額外的配置和功能
- 新增依賴: 如果你的專案是一個 Maven 專案,確保你的
pom.xml
檔案中包含了 Spring Boot 的依賴 - 配置執行緒池: 在 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()); }); } }