executor在Java中扮演線程管理和任務調度的核心角色,它解耦了任務提交與執行機制。1. newfixedthreadpool創建固定大小線程池,適用于任務數量穩定且對響應時間有要求的場景;2. newcachedthreadpool創建可動態調整大小的線程池,適合處理大量短期任務;3. newsinglethreadexecutor創建單線程池,保證任務順序執行;4. newscheduledthreadpool創建支持定時和周期性任務的線程池。但不推薦使用executors直接創建線程池,因可能引發oom問題,建議通過threadpoolexecutor自定義,其參數包括核心線程數、最大線程數、空閑線程存活時間、任務隊列、線程工廠及拒絕策略。關閉線程池時應優先調用shutdown()并配合awaittermination()確保任務完成,必要時再調用shutdownnow()強制關閉。
Executor在Java中扮演著線程管理和任務調度的核心角色,它允許我們將任務的提交與任務的執行機制解耦。簡單來說,Executor就是一個執行Runnable任務的對象,它隱藏了線程創建和管理的復雜性,讓開發者可以專注于業務邏輯。
線程池Executor的4種創建方式
Executors.newFixedThreadPool(int nThreads)
這是最常見的線程池創建方式之一。它創建一個固定大小的線程池,池中的線程數量始終保持不變。如果所有線程都在忙碌,新提交的任務將在隊列中等待,直到有線程空閑出來。
立即學習“Java免費學習筆記(深入)”;
- 優點: 資源消耗可控,響應速度快,適用于任務數量相對穩定,對響應時間有要求的場景。
- 缺點: 如果任務提交速度遠大于處理速度,隊列可能會無限增長,導致內存溢出。
ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 100; i++) { executor.execute(() -> { try { Thread.sleep(100); // 模擬耗時操作 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread: " + Thread.currentThread().getName()); }); } executor.shutdown(); // 記得關閉線程池
Executors.newCachedThreadPool()
創建一個可緩存的線程池。線程池的大小是不固定的,可以根據任務的需要動態調整。如果沒有空閑線程,則創建新的線程;如果線程空閑時間超過一定閾值(默認60秒),則會被回收。
- 優點: 可以充分利用系統資源,處理大量短期任務。
- 缺點: 如果任務提交速度過快,可能會創建大量的線程,導致CPU占用率過高,甚至OOM。不太適合長時間運行且任務量持續較高的場景。
ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) { final int taskNum = i; executor.execute(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread: " + Thread.currentThread().getName() + ", Task: " + taskNum); }); } executor.shutdown();
Executors.newSingleThreadExecutor()
創建一個單線程的Executor。它確保所有的任務都按照提交的順序依次執行。
- 優點: 保證任務的順序執行,避免并發問題。
- 缺點: 性能較低,不適合處理大量并發任務。
ExecutorService executor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int taskNum = i; executor.execute(() -> { System.out.println("Thread: " + Thread.currentThread().getName() + ", Task: " + taskNum); }); } executor.shutdown();
Executors.newScheduledThreadPool(int corePoolSize)
創建一個可以執行延遲任務或定時任務的線程池。
- 優點: 可以方便地執行定時任務。
- 缺點: 相對復雜,需要仔細配置參數。
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5); // 延遲1秒后執行 executor.schedule(() -> { System.out.println("Delayed task executed."); }, 1, TimeUnit.SECONDS); // 每隔2秒執行一次 executor.scheduleAtFixedRate(() -> { System.out.println("Periodic task executed."); }, 0, 2, TimeUnit.SECONDS); // 注意:ScheduledExecutorService通常需要手動關閉,否則程序可能不會退出 // executor.shutdown(); // 在適當的時候關閉
如何選擇合適的Executor?
選擇合適的Executor取決于具體的應用場景。例如,對于CPU密集型任務,可以考慮使用newFixedThreadPool,線程數量設置為CPU核心數+1。對于IO密集型任務,可以使用newCachedThreadPool,讓線程池根據需要動態調整大小。如果需要保證任務的順序執行,可以使用newSingleThreadExecutor。如果需要執行定時任務,可以使用newScheduledThreadPool。
為什么不推薦使用Executors直接創建線程池?
阿里巴巴Java開發手冊中建議不要使用Executors創建線程池,而是通過ThreadPoolExecutor的構造方法來創建。這是因為Executors提供的線程池在某些情況下可能會導致OOM。例如,newFixedThreadPool和newSingleThreadExecutor的LinkedBlockingQueue默認長度是Integer.MAX_VALUE,可能會堆積大量的請求,導致OOM。newCachedThreadPool雖然可以動態創建線程,但是如果沒有控制好線程的數量,也可能會導致OOM。
ThreadPoolExecutor的參數詳解
ThreadPoolExecutor是創建線程池的核心類。它的構造方法如下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
- corePoolSize: 核心線程數。線程池中始終保持的線程數量,即使它們是空閑的。
- maximumPoolSize: 最大線程數。線程池中允許的最大線程數量。
- keepAliveTime: 線程空閑時間。當線程池中的線程數量超過corePoolSize時,多余的空閑線程的存活時間。
- unit: keepAliveTime的時間單位。
- workQueue: 任務隊列。用于保存等待執行的任務。常見的隊列類型有LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue等。
- threadFactory: 線程工廠。用于創建新的線程。
- handler: 拒絕策略。當任務隊列已滿且線程池中的線程數量達到maximumPoolSize時,新提交的任務將被拒絕。常見的拒絕策略有AbortPolicy(拋出異常)、CallerRunsPolicy(由調用者線程執行)、DiscardPolicy(直接丟棄)、DiscardOldestPolicy(丟棄隊列中最老的任務)。
如何優雅地關閉線程池?
關閉線程池通常有兩種方式:shutdown()和shutdownNow()。
- shutdown(): 平滑關閉線程池,不再接受新的任務,但會等待所有已提交的任務執行完畢。
- shutdownNow(): 立即關閉線程池,嘗試停止所有正在執行的任務,并返回等待執行的任務列表。
建議使用shutdown()方法,給線程池一個優雅關閉的機會。在關閉線程池之后,需要調用awaitTermination()方法等待所有任務執行完畢。
ExecutorService executor = Executors.newFixedThreadPool(10); // 提交任務... executor.shutdown(); // 拒絕接受新的任務 try { if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); // 超時后強制關閉 } } catch (InterruptedException e) { executor.shutdownNow(); Thread.currentThread().interrupt(); }