對線程池使用的一些探索

最近在項目過程當中,有了一些對線程池使用的經驗。線程

1.如何等待線程池中的所有任務執行完設計

能夠經過ExecutorService的awaitTermination方法。在調用線程池的shutdown()方法後,再調用線程池的awaitTermination()方法,此方法會一直阻塞設置的時間,時間到後,若是線程池中的任務都已經執行完,返回true,否者返回false。利用while循環能夠將讓程序在線程池中的worker都執行完再執行後續的代碼。隊列

ExecutorService pool = ThreadPool.getLanguagePool();
pool.shutdown();
try {
    //每隔20秒檢查一次任務是否所有執行完
    while (!pool.awaitTermination(WAIT_TIME, TimeUnit.SECONDS)) {
        System.out.println("程序執行耗時" + ((System.currentTimeMillis() - start) / 1000) + "秒,還未結束");
    }
} catch (InterruptedException e) {
    System.out.println("任務被中斷");
    e.printStackTrace();
}

2.如何判斷線程池中的worker都執行完get

簡單的需求中能夠在關閉線程池後,經過awaitTermination()方法等待。但若是主任務會產生一些子任務,而後將子任務提交到同一個線程池,則必須等到池中的所有主任務都執行完,才能調用shutdown()方法,不然,在線程池處於拒絕新任務狀態,還在執行中的主任務一旦提交了子任務,將拋出RejectedExecutionException。同步

對於cached線程池,由於workers隊列中的thread在沒有新任務產生60秒後,thread就會被回收銷燬,因此能夠經過獲取workers隊列大小來判斷池中是否還有任務,任務所有執行完,就不存在主任務再提交子任務的狀況了。it

ThreadPoolExecutor pe = (ThreadPoolExecutor) pool;
System.out.println("工做線程池大小:" + pe.getPoolSize());
while (pe.getPoolSize() != 0) {
    System.out.println("工做線程池,如今大小爲:" + pe.getPoolSize());
    //等待cacheThreadPool中的線程所有執行完成
    threadSleep();
}
System.out.println("工做線程池,任務所有執行完畢,能夠關閉");
pool.shutdown();

對於fixed線程池,由於它在task隊列爲空,剩餘任務數量小於coreSize大小後,不會將thread回收,將workers數量降到coreSize如下,所以getPoolSize返回值一直不會爲0。對此能夠採用的方法是,設置一個全局的AtomicInteger計數器,在建立一個主任務時+1,主任務執行完畢-1,而後利用while循環和主線程sleep,能夠一直檢查主任務是否都執行結束,都結束後,再關閉線程池(還有其餘方法最後會提到)。io

其實,能夠將子任務提交到其餘的線程池。這樣當主任務都提交後,就能夠關閉線程池了。thread

3.對自定義線程池的考慮List

在項目中,我發現有些任務若是提交到cached線程池,任務太多時由於每一個任務的執行時間都很短,將減低程序吞吐量。若是提交到fixed線程池,當這些任務執行完畢後,將有coreSize數量的thread不能被回收,它們一直在阻塞隊列的take()上等待新任務被提交到線程池,一直處於WAITING狀態。爲了求到一個平衡,須要用自定義線程池。線程池

int corePoolSize, 線程核心池大小,當線程數量達到此值後,將不會減少,爲了能回收全部線程,將其設置爲0;
int maximumPoolSize,最大線程數量,爲了保證每一個task都有足夠的cpu時間,須要控制此最大值。這個值的設計須要考慮任務中cpu時間和其餘阻塞時間的比值,阻塞時間越多,就可讓更多的線程來執行任務,避免task線程都在阻塞,浪費cpu時間。個人電腦是至強cpu,支持32個線程執行,我設置值是32 * 3。
long keepAliveTime,線程空閒多少時間後回收線程,可使用cached池60秒的設計。
TimeUnit unit,空閒時間的單位。
BlockingQueue<Runnable> workQueue,任務隊列的類型,fixed池用的是底層爲linkedList的無界阻塞隊列。cached池用的是同步阻塞隊列,提交任務的線程,必須等到有worker線程取走任務才能返回,獲取任務的線程,也必須等待有線程提交了任務才能取走返回,因此這個池實際大小爲0。我用的是fixed池的LinkedBlockingQueue。

ThreadFactory,線程工廠,實現它主要是爲了設置線程的name,這樣看線程棧要方便不少。

最後,個人線程池以下

new ThreadPoolExecutor(MINI_THREAD_SIZE/*0*/, MAX_THREAD_SIZE/*96*/, 30, TimeUnit.SECONDS,
        new LinkedBlockingQueue<Runnable>(), new TaskFactory("mail"));

其實,coreSize能夠不設置爲0,經過調用線程池的

allowCoreThreadTimeOut(boolean value)

方法,能夠設置是否容許線程超時後被回收(默認是false),這樣當核心池一直空閒時,核心池線程也能被回收。

相關文章
相關標籤/搜索