1. 程式人生 > >關於執行緒池 ExecutorService 的總結

關於執行緒池 ExecutorService 的總結

一 、Java通過Executors提供四種執行緒池:

newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。
newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。
newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

二、 ExecutorService 的submit() 與execute()區別

1、接收的引數不一樣 submit()可以接受runnable和callable 有返回值,execute()接受runnable 無返回值

2、submit有返回值,而execute沒有

用到返回值的例子,比如說我有很多個做validation的task,我希望所有的task執行完,然後每個task告訴我它的執行結果,是成功還是失敗,如果是失敗,原因是什麼。

3、submit方便Exception處理

意思就是如果你在你的task裡會丟擲checked或者unchecked exception,而你又希望外面的呼叫者能夠感知這些exception並做出及時的處理,那麼就需要用到submit,通過捕獲Future.get丟擲的異常。

三、shotdown() showdownNow()區別

提供兩個方法來關閉 ExecutorService, 這將導致其拒絕新任務。
shutdown() 方法在終止前允許執行以前提交的任務,
shutdownNow() 方法阻止等待任務啟動並試圖停止當前正在執行的任務。在終止時執行程式沒有任務在執行,也沒有任務在等待執行,並且無法提交新任務。關閉未使用的 ExecutorService 以允許回收其資源。
一般分兩個階段關閉 ExecutorService。第一階段呼叫 shutdown 拒絕傳入任務,然後呼叫 shutdownNow(如有必要)取消所有遺留的任務

四、Runnable()與Callable()區別

如果是一個多執行緒協作程式,比如菲波拉切數列,1,1,2,3,5,8…使用多執行緒來計算。
但後者需要前者的結果,就需要用callable介面了。
callable用法和runnable一樣,只不過呼叫的是call方法,該方法有一個泛型返回值型別,你可以任意指定。

runnable介面實現的沒有返回值的併發程式設計。
這裡寫圖片描述

callable實現的存在返回值的併發程式設計。(call的返回值String受泛型的影響) 使用Future獲取返回值。
這裡寫圖片描述

(1). newCachedThreadPool
建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。示例程式碼如下:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
    final int index = i;
    try {
        Thread.sleep(index * 1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    cachedThreadPool.execute(new Runnable() {

        @Override
        public void run() {
            System.out.println(index);
        }
    });
}

執行緒池為無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的執行緒,而不用每次新建執行緒

(2). newFixedThreadPool
建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。示例程式碼如下:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
    final int index = i;
    fixedThreadPool.execute(new Runnable() {


        @Override
        public void run() {
            try {
                System.out.println(index);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}

因為執行緒池大小為3,每個任務輸出index後sleep 2秒,所以每兩秒列印3個數字。
定長執行緒池的大小最好根據系統資源進行設定。如Runtime.getRuntime().availableProcessors()。可參考PreloadDataCache。

(3) newScheduledThreadPool
建立一個定長執行緒池,支援定時及週期性任務執行。延遲執行示例程式碼如下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {

    @Override
    public void run() {
        System.out.println("delay 3 seconds");
    }
}, 3, TimeUnit.SECONDS);

表示延遲3秒執行。

定期執行示例程式碼如下:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

    @Override
    public void run() {
        System.out.println("delay 1 seconds, and excute every 3 seconds");
    }
}, 1, 3, TimeUnit.SECONDS);

表示延遲1秒後每3秒執行一次。
ScheduledExecutorService比Timer更安全,功能更強大,後面會有一篇單獨進行對比。

(4)、newSingleThreadExecutor
建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。示例程式碼如下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
    final int index = i;
    singleThreadExecutor.execute(new Runnable() {

        @Override
        public void run() {
            try {
                System.out.println(index);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}

結果依次輸出,相當於順序執行各個任務。
現行大多數GUI程式都是單執行緒的。Android中單執行緒可用於資料庫操作,檔案操作,應用批量安裝,應用批量刪除等不適合併發但可能IO阻塞性及影響UI執行緒響應的操作。  

  總結:

    (1)使用ExecutorService的submit函式由於execute函式

    (2)異常如何處理,異常後其他task停止