執行緒學習(3)
阿新 • • 發佈:2018-12-22
執行緒池
為了避免系統頻繁的建立和銷燬執行緒,我們可以將建立的執行緒進行復用。資料庫中的資料庫連線池也是此意。以下是執行緒池的優點
簡書執行緒池的使用細節
阿里巴巴外掛安裝說明
降低資源消耗。通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。 提高響應速度。當任務到達時,任務可以不需要等到執行緒建立就能立即執行。 提高執行緒的可管理性。執行緒是稀缺資源,如果無限制地建立,不僅會消耗系統資源,還會降低系統的穩定性, 使用執行緒池可以進行統一分配、調優和監控。但是,要做到合理利用執行緒池,必須對其實現原理了如指掌。 Executors 類中的常用的幾個常用的執行緒池類: public static ExecutorService newFixedThreadPool() 該方法返回一個固定執行緒數量的執行緒池 public static ExecutorService newSingleThreadExecutor() 返回一個只有一個現成的執行緒池 public static ExecutorService newCachedThreadPool() 根據實際情況調整執行緒數量的執行緒池 public static ScheduledExecutorService newSingleThreadScheduledExecutor() 該方法和 newSingleThreadExecutor 的區別是給定了時間執行某任務的功能,可以進行定時執行等; public static ScheduledExecutorService newScheduledThreadPool() 在4的基礎上可以指定執行緒數量 一個簡單的例子 public class ThreadPoolDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { int index = i; executorService.submit(() -> System.out.println("i:" + index + " executorService")); } executorService.shutdown(); } } 但是alibaba外掛會提示: 執行緒池不允許使用Executors去建立,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確執行緒池的執行規則,規避資源耗盡的風險。 Description 說明:Executors各個方法的弊端: 1)newFixedThreadPool和newSingleThreadExecutor: 主要問題是堆積的請求處理佇列可能會耗費非常大的記憶體,甚至OOM。 2)newCachedThreadPool和newScheduledThreadPool: 主要問題是執行緒數最大數是Integer.MAX_VALUE,可能會建立數量非常多的執行緒,甚至OOM。 即我們應當使用引數更加詳細的執行緒池建立方法: ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) 各個引數的細節: corePoolSize: 執行緒池維護執行緒的最少數量 maximumPoolSize:執行緒池維護執行緒的最大數量 keepAliveTime: 執行緒池維護執行緒所允許的空閒時間 unit: 執行緒池維護執行緒所允許的空閒時間的單位 workQueue: 執行緒池所使用的緩衝佇列 handler: 執行緒池對拒絕任務的處理策略 如果此時執行緒池中的數量小於corePoolSize,即使執行緒池中的執行緒都處於空閒狀態,也要建立新的執行緒來處理被新增的任務。 如果此時執行緒池中的數量等於 corePoolSize,但是緩衝佇列 workQueue未滿,那麼任務被放入緩衝佇列。 如果此時執行緒池中的數量大於corePoolSize,緩衝佇列workQueue滿,並且執行緒池中的數量小於maximumPoolSize,建新的執行緒來處理被新增的任務。 如果此時執行緒池中的數量大於corePoolSize,緩衝佇列workQueue滿,並且執行緒池中的數量等於maximumPoolSize,那麼通過 handler所指定的策略來處理此任務。 也就是:處理任務的優先順序為: 核心執行緒corePoolSize、任務佇列workQueue、最大執行緒maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。
submit 和 execute
-
submit有返回值,而execute沒有
-
submit方便Exception處理,在你的task裡會丟擲checked或者unchecked exception,而你又希望外面的呼叫者能夠感知這些exception並做出及時的處理,那麼就需要用到submit,通過捕獲Future.get丟擲的異常,注意是submit呼叫getfuture才行
public class ThreadPoolDemo3 { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(4); for (int i = 0; i < 5; i++) { int index = i; executorService.submit(() -> divTask(100, index)); } executorService.shutdown(); } private static void divTask(int a, int b) { double result = a / b; System.out.println(result); } } 執行結果: 100.0 25.0 33.0 50.0 可以看到錯誤被內部捕獲了 而採用: public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(4); for (int i = 0; i < 5; i++) { int index = i; Future future = executorService.submit(() -> divTask(200, index)); try { future.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } executorService.shutdown(); } private static void divTask(int a, int b) { double result = a / b; System.out.println(result); } 執行結果: java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:192) at gitchat.ThreadPoolDemo4.main(ThreadPoolDemo4.java:17) Caused by: java.lang.ArithmeticException: / by zero at gitchat.ThreadPoolDemo4.divTask(ThreadPoolDemo4.java:26) at gitchat.ThreadPoolDemo4.lambda$0(ThreadPoolDemo4.java:15) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) 200.0 100.0 66.0 50.0