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

ThreadPoolExecutor工作原理
當主線程中調用execute接口提交執行任務時:
則執行以下步驟:
注意:線程池初始時,是空的。
- 如果當前線程數<corePoolSize,如果是則創建新的線程執行該任務
- 如果當前線程數>=corePoolSize,則將任務存入BlockingQueue<Runnable>
- 如果阻塞隊列已滿,且當前線程數<maximumPoolSize,則新建線程執行該任務。
- 如果阻塞隊列已滿,且當前線程數>=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