1. 程式人生 > >(四)java 執行緒,執行緒池的使用

(四)java 執行緒,執行緒池的使用

為什麼要使用執行緒池?

   建立執行緒是簡單的,但啟動後的執行緒猶如脫繮野馬,難於管理,特別是多執行緒使用場景,執行緒之間的互相競爭,可能使 cpu 花費更多時間在各個執行緒之間切換,而且執行緒結束後的回收由垃圾回收控制,你不知道工作結束的執行緒還會存活多久,是否持有著什麼資源。而且執行緒物件提供使用方法有限,無法提供定時啟動、執行緒併發數控制等操作,所以,執行緒池出現了。
   執行緒池提供了對執行緒的管理,讓執行緒稍微能得到控制(中斷、取消等);併發執行緒數的控制,提高系統資源使用率,避免過多的資源競爭;複用執行緒,減少執行緒物件建立,提高執行緒啟動效率以及降低系統資源消耗;提供延時啟動、定期執行等操作。

執行緒池介紹

具體執行緒池有以下幾個:

  • ThreadPoolExecutor
    繼承 AbstractExecutorServiceExecutors 提供的預設執行緒池,除了定時執行緒池,其他都是ThreadPoolExecutor的預設引數構建
  • ScheduledThreadPoolExecutor可排程執行緒池(定時執行緒池)
    繼承 ThreadPoolExecutor實現ScheduledExecutorService
  • ForkJoinPool ,繼承 AbstractExecutorService
    特殊執行緒池,分治法(Divide-and-Conquer Algorithm),可以將一個大任務(執行緒)在細分多個小任務執行,直到所有小任務完成才算大任務完成,充分利用多核 cpu 的特性。與 ThreadPoolExecutor

    區別,ForkJoinPool可以實現在有限的執行緒數下完成非常多的任務,如使用4個執行緒完成具有父子關係的400萬個任務,而 ThreadPoolExecutor 無法選擇優先執行子任務,所以處理400萬的任務,需要400萬個執行緒。

  • ExecutorCompletionService
    實現 CompletionService,構造方法傳入 Executor,可以認為是對 Executor 的使用封裝。

使用較多的執行緒池是 ThreadPoolExecutor,本文只介紹這個。對其他型別有興趣的可以去 java.util.concurrent 中檢視。

構造方法:

public
ThreadPoolExecutor(int corePoolSize,//核心池數量,併發的執行緒數 int maximumPoolSize,//執行緒池最大容量 long keepAliveTime,//執行緒存活時間 TimeUnit unit,//時間單位 BlockingQueue<Runnable> workQueue,//執行緒快取佇列 ThreadFactory threadFactory,//執行緒建立工廠 RejectedExecutionHandler handler//加入執行緒失敗策略) { //... 省略 }

向執行緒池提交一個任務,如果核心池未滿,則用該命令作為第一個任務開啟一個新執行緒

if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))//true:新增成功 false:新增失敗
        return;
    c = ctl.get();
}

如果核心池已經滿了,檢查執行緒池最大容量,沒有超過,則加入快取工作佇列,建立新執行緒去執行任務;當執行緒池達到最大容量,並且快取佇列已經飽和時,此時再繼續往執行緒池中新增任務,則會拒絕。
由快取佇列建立的執行緒,如果處於空閒狀態,在一定的時間(keepAliveTime)後將會被回收,而核心池中的執行緒如果 allowCoreThreadTimeOut 該引數為 false(預設值) 即使處於空閒狀態也不會被回收。

流程圖如下:

使用:

ThreadPoolExecutor pool = new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
pool.execute(()-> print "runnable");

一般來說,除非有特殊的處理要求,否則建議使用Executors 中的方法構造執行緒池,對於執行緒池的核心數、最大容量、工作佇列結構、執行緒工廠等設定,預設構造基本滿足開發需求。

關閉執行緒池:

//不再接受新任務,直到快取區任務執行完畢
public void shutdown(){...}
//不再接受新任務,將快取區任務返回,並清空工作區,中斷執行中的任務
public List<Runnable> shutdownNow() {...}