Java中Executors類的用途 掌握線程池工廠的創建方法

如何使用executors創建線程池?1.使用newfixedthreadpool(int nthreads)創建固定大小的線程池;2.使用newcachedthreadpool()創建可緩存線程池;3.使用newsinglethreadexecutor()創建單線程線程池;4.使用newscheduledthreadpool(int corepoolsize)創建支持定時和周期任務的線程池。線程池的拒絕策略是什么?如何自定義拒絕策略?默認使用abortpolicy,其他內置策略包括callerrunspolicy、discardpolicy和discardoldestpolicy,自定義需實現rejectedexecutionhandler接口并在創建線程池時傳入。如何優雅地關閉線程池?調用shutdown()平滑關閉,調用shutdownnow()立即停止任務,結合awaittermination()等待任務完成或超時處理。如何監控線程池的狀態?通過getpoolsize()、getactivecount()、getcompletedtaskcount()、gettaskcount()和getqueue().size()獲取狀態信息。executors創建的線程池有什么缺點?其隊列容量過大可能導致oom,newcachedthreadpool可能創建過多線程耗盡資源,推薦手動使用threadpoolexecutor創建以精確控制參數并避免風險。

Java中Executors類的用途 掌握線程池工廠的創建方法

Executors類是Java并發包中一個強大的工具類,它簡化了線程池的創建和管理,提供了一系列靜態工廠方法,幫助開發者快速構建各種類型的線程池,而無需深入了解線程池的底層細節。

Java中Executors類的用途 掌握線程池工廠的創建方法

Executors類主要用于創建不同類型的線程池,它隱藏了線程池的復雜性,使開發者能夠更專注于任務的執行。

Java中Executors類的用途 掌握線程池工廠的創建方法

如何使用Executors創建線程池?

Executors類提供了多種靜態工廠方法,用于創建不同類型的線程池,以下是一些常用的方法:

立即學習Java免費學習筆記(深入)”;

  • newFixedThreadPool(int nThreads): 創建一個固定大小的線程池,池中始終保持指定數量的線程。如果所有線程都在忙碌,新的任務將被放入隊列中等待。

    Java中Executors類的用途 掌握線程池工廠的創建方法

    ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 100; i++) {     executor.execute(new Task(i)); } executor.shutdown();
  • newCachedThreadPool(): 創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需的數量,會自動回收空閑線程。當有新任務提交時,如果沒有空閑線程可用,則會創建新的線程。這種線程池非常適合處理大量的短期異步任務。

    ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) {     executor.execute(new Task(i)); } executor.shutdown();
  • newSingleThreadExecutor(): 創建一個單線程的線程池。所有提交的任務都會按照先進先出的順序執行。如果線程在執行任務時發生異常,會創建一個新的線程來替代它,保證所有任務都會被執行。

    ExecutorService executor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 100; i++) {     executor.execute(new Task(i)); } executor.shutdown();
  • newScheduledThreadPool(int corePoolSize): 創建一個可以執行定時任務和周期性任務的線程池。

    ScheduledExecutorService executor = Executors.newScheduledThreadPool(5); executor.scheduleAtFixedRate(new Task(1), 1, 3, TimeUnit.SECONDS); // 每隔3秒執行一次,延遲1秒后開始執行

線程池的拒絕策略是什么?如何自定義拒絕策略?

當線程池的任務隊列已滿,且線程池中的所有線程都在忙碌時,如果再有新的任務提交,線程池會使用拒絕策略來處理這些任務。Executors創建的線程池默認使用ThreadPoolExecutor.AbortPolicy,該策略會直接拋出RejectedExecutionException異常。

Java提供了四種內置的拒絕策略:

  • AbortPolicy: 默認策略,直接拋出RejectedExecutionException異常。
  • CallerRunsPolicy: 由提交任務的線程來執行該任務。
  • DiscardPolicy: 直接丟棄該任務,不拋出異常。
  • DiscardOldestPolicy: 丟棄隊列中最老的未處理任務,然后嘗試執行當前任務。

當然,你也可以自定義拒絕策略,只需要實現RejectedExecutionHandler接口即可:

import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor;  public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {     @Override     public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {         System.out.println("Task " + r.toString() + " rejected from " + executor.toString());         // 可以添加自定義的拒絕處理邏輯,例如記錄日志、持久化任務等     } }

然后,在創建線程池時,將自定義的拒絕策略傳遞給ThreadPoolExecutor的構造函數

import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;  public class Main {     public static void main(String[] args) {         int corePoolSize = 5;         int maxPoolSize = 10;         long keepAliveTime = 5000;         ThreadPoolExecutor executor = new ThreadPoolExecutor(                 corePoolSize,                 maxPoolSize,                 keepAliveTime,                 TimeUnit.MILLISECONDS,                 new LinkedBlockingQueue<>(100), // 任務隊列                 new CustomRejectedExecutionHandler() // 自定義拒絕策略         );          for (int i = 0; i < 200; i++) {             executor.execute(new Task(i));         }          executor.shutdown();     } }

如何優雅地關閉線程池?shutdown()和shutdownNow()有什么區別

關閉線程池是一個重要的步驟,它可以防止資源泄漏。Executors創建的線程池提供了兩種關閉方法:shutdown()和shutdownNow()。

  • shutdown(): 平滑關閉線程池。它會阻止新的任務提交,但會等待所有已提交的任務執行完成。

  • shutdownNow(): 立即關閉線程池。它會嘗試停止所有正在執行的任務,并返回一個包含所有等待執行的任務的列表。需要注意的是,shutdownNow()方法并不能保證所有任務都會被立即停止,因為某些任務可能正在執行一些無法中斷的操作。

在調用shutdown()或shutdownNow()方法后,可以使用awaitTermination()方法來等待線程池中的所有任務執行完成,或者等待指定的超時時間。

ExecutorService executor = Executors.newFixedThreadPool(10); // 提交任務... executor.shutdown(); // 阻止新的任務提交 try {     if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { // 等待60秒         executor.shutdownNow(); // 如果超時,則嘗試立即停止所有任務     } } catch (InterruptedException e) {     executor.shutdownNow(); // 如果當前線程被中斷,則嘗試立即停止所有任務 }

如何監控線程池的狀態?

監控線程池的狀態對于診斷性能問題和優化線程池配置非常重要。ThreadPoolExecutor類提供了一些方法來獲取線程池的狀態信息:

  • getPoolSize(): 返回線程池中的線程數量。
  • getActiveCount(): 返回正在執行任務的線程數量。
  • getCompletedTaskCount(): 返回已完成的任務數量。
  • getTaskCount(): 返回已提交的任務總數。
  • getQueue().size(): 返回任務隊列中的任務數量。

你可以使用這些方法來定期監控線程池的狀態,并根據需要調整線程池的配置。例如,如果getQueue().size()持續增長,說明任務提交的速度超過了線程池的處理能力,可能需要增加線程池的大小。

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10); // 提交任務...  while (true) {     System.out.println("Pool Size: " + executor.getPoolSize());     System.out.println("Active Count: " + executor.getActiveCount());     System.out.println("Completed Task Count: " + executor.getCompletedTaskCount());     System.out.println("Task Count: " + executor.getTaskCount());     System.out.println("Queue Size: " + executor.getQueue().size());     Thread.sleep(1000); // 每隔1秒打印一次狀態信息 }

Executors創建的線程池有什么缺點?為什么推薦使用ThreadPoolExecutor手動創建?

雖然Executors類簡化了線程池的創建,但它也存在一些缺點:

  • newFixedThreadPool()和newSingleThreadExecutor(): 它們的任務隊列使用了LinkedBlockingQueue,該隊列的默認容量是Integer.MAX_VALUE。如果任務提交的速度超過了線程池的處理能力,會導致大量的任務積在隊列中,最終可能導致OOM(OutOfMemoryError)異常。

  • newCachedThreadPool(): 它可以創建無限數量的線程,如果任務提交的速度過快,可能會創建大量的線程,導致系統資源耗盡。

因此,阿里巴巴Java開發手冊中建議使用ThreadPoolExecutor手動創建線程池,而不是使用Executors類。手動創建線程池可以更精確地控制線程池的參數,例如核心線程數、最大線程數、任務隊列的類型和容量、拒絕策略等,從而避免潛在的風險。

import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;  public class Main {     public static void main(String[] args) {         int corePoolSize = 5;         int maxPoolSize = 10;         long keepAliveTime = 5000;         ThreadPoolExecutor executor = new ThreadPoolExecutor(                 corePoolSize,                 maxPoolSize,                 keepAliveTime,                 TimeUnit.MILLISECONDS,                 new LinkedBlockingQueue<>(100) // 任務隊列,設置容量         );          for (int i = 0; i < 200; i++) {             executor.execute(new Task(i));         }          executor.shutdown();     } }

總之,Executors類是Java并發編程中一個非常有用的工具,但需要了解其潛在的風險,并根據實際情況選擇合適的線程池創建方式。在大多數情況下,推薦使用ThreadPoolExecutor手動創建線程池,以便更好地控制線程池的參數,并避免潛在的OOM風險。

? 版權聲明
THE END
喜歡就支持一下吧
點贊14 分享