ThreadPoolExecutor的用法


Java中的線程池

  • 一般我們說起Java中的線程池,其實指的是java.util.concurrent包下的ThreadPoolExecutor。當然java包下還有其他線程池的實現類,但主要也是最常用的就是這個類。今天我們來好好說說這個類。
  • 這里我們結合了其他人的整理和自己的思考進行了總結。

1. 工作原理

如圖所示:


 
ThreadPoolExecutor工作原理

當主線程中調用execute接口提交執行任務時:
則執行以下步驟:
注意:線程池初始時,是空的。

  1. 如果當前線程數<corePoolSize,如果是則創建新的線程執行該任務
  2. 如果當前線程數>=corePoolSize,則將任務存入BlockingQueue<Runnable>
  3. 如果阻塞隊列已滿,且當前線程數<maximumPoolSize,則新建線程執行該任務。
  4. 如果阻塞隊列已滿,且當前線程數>=maximumPoolSize,則拋出異常RejectedExecutionException,告訴調用者無法再接受任務了。
注意點:
  • 線程池初始化時,是空的。
  • 如果阻塞隊列已滿,且當前線程數<maximumPoolSize,則新建線程執行該任務。而不是新建線程,從阻塞隊列里take任務來執行,所以這里並不是先來先執行的。
提問:這里的阻塞隊列是BlockingQueue<Runnale>,我們知道ThreadPoolExecutor是支持Callable任務提交的,那這里不會有問題嗎?
  • 答:其實我們這里說的都是execute接口提交任務。execute接口只接受Runnable。其實我們看一下繼承關系圖:


     
    ThreadPoolExecutor類圖
  • 可以看到其實ThreadPoolExecutor是繼承了AbstractExecutorService,而AbstractExecutorService實現了ExecutorService。

  • 且我們可以看到ThreadPoolExecutor類里只重寫了execute方法。ExecutorService的其他方法都沒有實現,而是在AbstractExecutorService里實現的。所以說整個ThreadPoolExecutor里的策略都是只針對execute方法來說的。所以說上述的工作原理只針對execute接口。像其他的submit/invoke接口並不適用--因為調用這些接口其實調用的是AbstractExecutorService的實現。

2. 我們來看一下ThreadPoolExecutor的參數

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 
  • corePoolSize:就是上圖中的CorePoolSize,意為核心線程數。
  • maximumPoolSize:就是上圖中maximumPoolSize,意為整個線程池的最大線程數。這個數字是包含orePoolSize的。
  • keepAliveTime和unit:是idle線程最大存活時間。unit是前者的單位。即如果當前線程數>corePoolSize,則會kill一些線程至corePoolSize大小。
  • workQueue:就是上面說的阻塞隊列,注意BlockingQueue<Runnable>只是個接口,下面我們會細說一下它的常用實現類。
  • 下面兩個參數是可選項:即Java重載了ThreadPoolExecutor的構造方法,下面兩個參數可以不傳,也可以只傳一個。
  • threadFactory:線程工廠。創建線程的接口,該接口只有一個方法:Thread newThread(Runnable r);我們可以實現這個接口進而自定義創建線程,比如制定線程名稱,線程組等。
  • handler:當線程池已滿時,再提交任務會觸發調用這個回調函數。RejectedExecutionHandler接口的唯一方法:
void rejectedExecution(Runnable r, ThreadPoolExecutor executor); 

3. 阻塞隊列的常用實現類

  • ArrayBlockingQueue: 有邊界的阻塞隊列。長度大小初始化時制定,即內部由數組實現。
  • LinkedBlockingQueue: 有邊界的阻塞隊列。邊界是可選的,如果初始化的時候不指定則默認是Interger.MAX_VALUE,內部由鏈表實現。(最常用)
  • PriorityBlockQueue: 帶有優先級的阻塞隊列。沒有邊界。
  • 這三個類都實現了BlockingQueue接口,其中LinkedBlockingQueue最常用,特別注意一般一定要指定邊界大小,不然線程池失去了些意義,且造成內存泄露。
    這里就不對這些細說,以后我們再細說。

4. RejectedExecutionHandler線程池飽和策略

  • 默認的 ThreadPoolExecutor.AbortPolicy:處理程序遭到拒絕將拋出運行時RejectedExecutionException。
  • ThreadPoolExecutor.CallerRunsPolicy:調用線程直接call該任務的execute 本身。
  • ThreadPoolExecutor.DiscardPolicy:將任務刪除。
  • ThreadPoolExecutor.DiscardOldestPolicy刪除工作隊列頭部任務。

5. ThreadPoolExecutor的擴展

我們看ThreadPoolExecutor的源碼發現如下三個函數的實現為空且是protected。明顯用於子類實現的。

protected void beforeExecute(Thread t, Runnable r) { } protected void afterExecute(Runnable r, Throwable t) { } protected void terminated() { } 
  • 在執行任務的線程中將調用beforeExecute和afterExecute等方法,在這些方法中還可以添加日志、計時、監視或者統計信息收集的功能。
  • 無論任務是從run中正常返回,還是拋出一個異常而返回,afterExecute都會被調用。如果任務在完成后帶有一個Error,那么就不會調用afterExecute。
  • 如果beforeExecute拋出一個RuntimeException,那么任務將不被執行,並且afterExecute也不會被調用。
  • 在線程池完成關閉時調用terminated,也就是在所有任務都已經完成並且所有工作者線程也已經關閉后,terminated可以用來釋放Executor在其生命周期里分配的各種資源,此外還可以執行發送通知、記錄日志或者手機finalize統計等操作。
    即我們可以子類來繼承ThreadPoolExecutor來定制化一些功能。


鏈接:https://www.jianshu.com/p/f9c6bf9bb543


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM