1. 程式人生 > >執行緒學習(3)

執行緒學習(3)

執行緒池

為了避免系統頻繁的建立和銷燬執行緒,我們可以將建立的執行緒進行復用。資料庫中的資料庫連線池也是此意。以下是執行緒池的優點
簡書執行緒池的使用細節
阿里巴巴外掛安裝說明

降低資源消耗。通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。

提高響應速度。當任務到達時,任務可以不需要等到執行緒建立就能立即執行。

提高執行緒的可管理性。執行緒是稀缺資源,如果無限制地建立,不僅會消耗系統資源,還會降低系統的穩定性,
使用執行緒池可以進行統一分配、調優和監控。但是,要做到合理利用執行緒池,必須對其實現原理了如指掌。

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