1. 程式人生 > >第六十八條 executor和task優先於執行緒

第六十八條 executor和task優先於執行緒

java比較早的時候,就提供了 WorkQueue WorkQueueImpl 這些類,它允許客戶端將後臺的非同步執行緒加入這個佇列,當不在需要這個工作佇列時,客戶端端可以呼叫一個方法,讓後臺執行緒完成了在佇列中的工作後,終止自己。我們自己在用這個功能時,需要特別小心注意,防止出錯。java 1.5 以後,java 平臺中增加了 java.util.concurrent,這個包中包含了Executor Framework,這是一個很靈活的基於介面的任務執行工具它建立了一個在各方面都很好的佇列,只需要一行這樣的程式碼:

ExecutorService executor = Executors.newSingleThreadExecutor();裡面時靜態方法,封裝好的執行緒池。
下面是為執行提交 runnable 的方法:
executor.execute(runnable);
下面是如何優雅的終止:
executor .shutdown();


    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }


看這個方法,我們知道比較關鍵的是 new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()) 這個物件,這是生產一個執行緒池,簡單介紹一下里面引數的意思,這個構造方法是

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

corePoolSize    核心執行緒數,預設情況下核心執行緒會一直存活,即使處於閒置狀態也不會受存keepAliveTime限制,除非將 allowCoreThreadTimeOut 設定為true,否則一致存活。


maximumPoolSize 執行緒池所能容納的最大執行緒數,超過這個數的執行緒將被阻塞,執行緒加workQueue中,等待前面執行緒執行完後,從queue中取出執行緒繼續執行。


keepAliveTime  非核心執行緒的閒置超時時間,超過這個時間非核心執行緒就會被回收。


unit  指定keepAliveTime的單位,如TimeUnit.SECONDS,意思是單位為分鐘。主要是對非核心執行緒生效,當將allowCoreThreadTimeOut設定為true時也對corePoolSize生效。


workQueue  執行緒池中的任務佇列。常用的有三種佇列,SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue。LinkedBlockingQueue在構造方法中如果沒有沒有傳入大小限制,預設無限大

後面兩個引數是用的預設值,這裡只是簡單介紹一下,不做過多的敘述。


瞭解了上面的意思,再看看Executors中其他的靜態方法。平常,我們在處理後臺執行緒任務時,可能會有各種需求。比如想用一個執行緒,或者固定數目的執行緒,或者越多越好,這時候我們就呼叫 Executors 的方法,它能返回各種功能的執行緒池,如果我們想要另類的功能,也可以直接用 ThreadPoolExecutor 來創造出想要的執行緒池。

如果編寫的是小程式,或者輕載的伺服器,使用Executors.newCachedThreadPool通常是個不錯的選擇,因為它不需要配置,並且一般情況下能夠正確的完成工作。但是對於大負載的伺服器來說,快取的執行緒池就不是很好的選擇了!在快取的執行緒池中,被提交的任務沒有排成佇列,而是直接交給執行緒執行。如果沒有執行緒可用,就建立一個新的執行緒。如果伺服器負載的太重,以致它所有的CPU都完全被佔用了,當有更多的任務時,就會建立更多的執行緒,這樣只會使情況變得更糟。因此,在大負載的產品伺服器中,最好使用Executors.newFixedThreadPool,它為你提供了一個包含固定執行緒數目的執行緒池,或者為了最大限度的控制它,就直接使用ThreadPoolExecutor類。

平常開發中,我們直接呼叫這個類就夠用了,基本沒必要自己手動實現一個執行緒池,或者編寫自己的工作佇列,甚至沒必要直接 new 一個 Thread,現在開啟一個執行緒時,可以把抽象的邏輯直接寫在 Runnable 或者 Callable 中,執行緒池可以直接執行 Runnable, 而 Callable 可以封裝到 Runnable 中。Runnable 是沒有返回結果的,Callable是有返回結果的,android中的 AsyncTask 就是利用執行緒池和 Runnable 及 Callable 實現後臺邏輯的,通過用Handler來切換到主執行緒。耗時操作在 Callable 的 call() 方法中執行,把 Callable 物件傳遞到FutureTask 中,當做它的屬性, FutureTask 是 Runnable 的實現類,然後用 Executor 的 execute(Runnable command) 方法執行 FutureTask, FutureTask 原始碼下次再分析,我們只需要知道,FutureTask 實現了 Runnable,在 run()方法中執行了 Callable 的 call() 方法,而 call() 中執行就是 AsyncTask 的 Result doInBackground(Params... params); 方法,這樣,執行完了,會呼叫 FutureTask 的 done() 方法,然後把 run() 方法中得到的資料,通過Handler,發給UI執行緒執行。這段程式碼很有意思,大夥可以看看。

Executor Framework也有一個可以代替java.util.Timer的東西,即ScheduledThreadPoolExecutor。雖然timer使用起來更加易,但是被排程的執行緒池executor更加靈活。timer只用一個執行緒來執行任務,如果timer唯一的執行緒丟擲未被捕獲的異常,timer就會停止執行。被排程的執行緒池executor支援多個執行緒,並且優雅的從丟擲未受檢異常的任務中恢復。