Executors 封裝的四種線程池 各自有何特點,如何使用 超詳細 代碼動圖演示
1.了解線程池 什么是線程池 為什么要用線程池
一種線程使用模式。線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。而線程池維護著多個線程,等待著監督管理者分配可并發執行的任務。這避免了在處理短時間任務時創建與銷毀線程的代價。線程池不僅能夠保證內核的充分利用,還能防止過分調度。可用線程數量應該取決于可用的并發處理器、處理器內核、內存、網絡sockets等的數量。 例如,線程數一般取cpu數量+2比較合適,線程數過多會導致額外的線程切換開銷 ----摘自 百度百科
扣下重點 :
線程池 是 一種 線程使用模式
頻繁創建線程帶來巨大的 調度開銷 影響性能
線程池 維護 多個線程 , 但維護的線程數 并不是越多越好
**線程池一種線程的使用模式,它幫線程使用者維護著多個線程 使用線程池可以 避免用戶頻繁創建線程帶來的巨大開銷,防止過分調度.但線程池維護的線程數并不是越多越好 需要我們合理的創建,使用線程池 因此 引出了 為什么不推薦使用 Executors 創建線程池 **
2.0 Executors的四種線程池 概覽
2.1 FixedThreadPool 定長線程池
2.1.1 創建一個FixedThreadPool 線程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool();
2.1.2 源碼解析
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
創建一個線程池,該線程池重用在共享無界隊列上運行的固定數量的線程。在任何時候,最多 nThreads 個線程將是活動的處理任務。如果在所有線程都處于活動狀態時提交了其他任務,它們將在隊列中等待,直到有線程可用。如果任何線程在關閉之前的執行過程中由于失敗而終止,如果需要執行后續任務,新的線程將取代它。池中的線程將一直存在,直到顯式關閉。參數:nThreads - 池中的線程數返回:新創建的線程池
Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. At any point, at most nThreads threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. If any thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks. The threads in the pool will exist until it is explicitly shutdown. Params: nThreads – the number of threads in the pool Returns: the newly created thread pool Throws: IllegalArgumentException – if nThreads <= 0
2.1.3 參數解析
nThreads 核心線程數
nThreads 最大線程數
0L 線程池中非核心線程空閑的存活時間大小
TimeUnit.MILLISECONDS 線程空閑存活時間單位
new LinkedBlockingQueue
public LinkedBlockingQueue() { this(Integer.MAX_VALUE); }
FixedThreadPool 線程池的 核心線程數 等于 最大線程池數 也就是 只能有核心線程數 且等待隊列 是 默認 隊列長度為 Integer.MAX_VALUE的 LinkedBlockingQueue 阻塞隊列
什么意思呢 看代碼演示
2.1.4 代碼演示
public class ExecutorsTest { public static void main(String[] args) { //創建核心線程數為10的 FixedThreadPool 線程池 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10, new ThreadFactory() { int t = 0; @Override public Thread newThread(Runnable r) { return new Thread(r,"線程 : "+(++t)); } }); int num = 0; for (int i = 0; i < 100; i++) { fixedThreadPool.execute(()->{ try { System.out.println(Thread.currentThread().getName()+"正在運行"); TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } }); } } }
我們創建一個核心線程數 為 10 的fixedThreadPool 線程池 循環提交 100個線程任務 可以看到動態圖中 只會同時有十個線程在執行
且永遠都是那10個線程在執行任務 為什么 先看下圖 線程池工作原理
核心線程創建后就會一直存活 由于 我們創建的 fixedThreadPool 線程池核心線程數為 10 且最大線程數為10 等待隊列的隊列長度為
integer.MAX_VALUE 所以 同時只能有10個線程任務執行 其余的全部進入等待隊列 核心線程執行完任務后再去隊列中取等待執行的任務
2.1.5 線程池的用途
由于 FixedThreadPool 只有核心線程,所以可以用來控制線程最大并發 無論來多少線程 我只執行核心線程 其余的全部進入等待隊列等待執行
2.2 ScheduledThreadPool 定時線程池
2.2.1 創建一個ScheduledThreadPool 線程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(corePoolSize);
2.2.2 源碼解析
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue
2.2.3 參數解析
corePoolSize 核心線程數
Integer.MAX_VALUE 最大線程數( 非核心線程數 )
0 線程池中非核心線程空閑的存活時間大小
NANOSECONDS 線程空閑存活時間單位
new DelayedWorkQueue() 無界的隊列,用于放置實現了Delayed接口的對象,其中的對象只能在其到期時才能從隊列中取走
2.2.4 代碼演示
public class ScheduledThreadPoolTest { public static void main(String[] args) { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); System.out.println(LocalTime.now()); //創建并執行在給定延遲后執行任務 scheduledExecutorService.schedule(()->{ System.out.println(LocalTime.now()); }, //延遲執行 3, TimeUnit.SECONDS ); } }
創建并執行在給定的初始延遲之后,隨后以給定的時間段首先啟用的周期性動作; 那就是執行將在initialDelay之后開始,然后initialDelay+period ,然后是initialDelay + 2 * period ,等等。 如果任務的執行遇到異常,則后續的執行被抑制。 否則,任務將僅通過取消或終止執行人終止。 如果任務執行時間比其周期長,則后續執行可能會遲到,但不會同時執行。
public class ScheduledThreadPoolTest { public static void main(String[] args) { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); System.out.println(LocalTime.now()); //周期執行任務 scheduledExecutorService.scheduleAtFixedRate(()->{ System.out.println(LocalTime.now()); }, //延遲第一次執行的時間 0, //連續執行之間的時間 3, //initialDelay和period參數的時間單位 TimeUnit.SECONDS ); } }
注意 : **如果任務執行時間比其周期長,則后續執行可能會遲到,但不會同時執行。 **
什么意思呢 ? 如果一個任務的執行時間超過 周期時間 下一個任務不會執行 會等待上一個任務執行完畢后再去計算周期
public class ScheduledThreadPoolTest { public static void main(String[] args) { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); //周期執行任務 scheduledExecutorService.scheduleAtFixedRate(()->{ System.out.println(LocalTime.now()); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } }, //延遲第一次執行的時間 0, //連續執行之間的時間 3, //initialDelay和period參數的時間單位 TimeUnit.SECONDS ); } }
2.2.5 線程池的用途
執行定時任務 或者 周期性 任務
2.3 CachedThreadPool 緩存線程池
2.3.1 創建一個CachedThreadPool 線程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
2.3.2 源碼解析
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue
Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks. Calls to execute will reuse previously constructed threads if available. If no existing thread is available, a new thread will be created and added to the pool. Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources. Note that pools with similar properties but different details (for example, timeout parameters) may be created using ThreadPoolExecutor constructors.
創建一個線程池,根據需要創建新線程,但在可用時將重用以前構造的線程。這些池通常會提高執行許多短期異步任務的程序的性能。如果可用,對執行的調用將重用以前構造的線程。如果沒有可用的現有線程,將創建一個新線程并將其添加到池中。六十秒內未使用的線程將被終止并從緩存中刪除。因此,保持空閑足夠長時間的池不會消耗任何資源。請注意,可以使用 ThreadPoolExecutor 構造函數創建具有相似屬性但細節不同(例如超時參數)的池。
2.3.3 參數解析
0 核心線程數
Integer.MAX_VALUE 最大線程數( 非核心線程數 )
60L 線程池中非核心線程空閑的存活時間大小
TimeUnit.SECONDS 線程空閑存活時間單位
new SynchronousQueue
2.3.4 代碼演示
public class CachedThreadPoolTest { public static void main(String[] args) { ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 1000; i++) { int finalI = i; cachedThreadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"執行了 "+ finalI); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } }); } } }
由于沒有核心線程 且等待隊列為 SynchronousQueue
所以提交的線程任務全部會交給非核心線程任務執行 非核心線程任務的存活時長為 60 秒
2.3.5 線程池的用途
適用于處理大量、耗時少的任務。 每次提交線程任務都會立即有非核心線程去執行
2.4 SingleThreadExecutor單線程化線程池
2.4.1 創建一個SingleThreadExecutor線程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
2.4.2 源碼解析
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
Creates an Executor that uses a single worker thread operating off an unbounded queue. (Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.) Tasks are guaranteed to execute sequentially, and no more than one task will be active at any given time. Unlike the otherwise equivalent newFixedThreadPool(1) the returned executor is guaranteed not to be reconfigurable to use additional threads.
創建一個使用單個工作線程在無界隊列上運行的 Executor。 (但請注意,如果該單線程在關閉前的執行過程中因失敗而終止,則如果需要執行后續任務,則新線程將取代它。)任務保證按順序執行,并且不會有多個任務處于活動狀態在任何給定時間。與其他等效的 newFixedThreadPool(1) 不同,返回的執行程序保證不可重新配置以使用額外的線程。
2.4.3 參數解析
1 核心線程數
1 最大線程數( 非核心線程數 )
0L 線程池中非核心線程空閑的存活時間大小
TimeUnit.MILLISECONDS 線程空閑存活時間單位
new LinkedBlockingQueue
2.4.4 代碼演示
public class SingleThreadExecutorTest { public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 100; i++) { executorService.execute(()->{ System.out.println(Thread.currentThread().getName()); }); } } }
永遠只有一個核心線程去執行線程任務
2.4.5 線程池的用途
所有的線程任務都將使用同一個線程( 核心線程創建后一直存活 ).在隊列中有序等待執行
任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。