Java線程池總結
一、什麼是線程池?
線程池(百度百科):一種線程管理模式。線程維護着多個線程,等待着分配可併發執行的任務。這避免了在處理短時間任務時創建和銷燬線程的代價,進而影響影響緩存性能。線程池不僅不僅保證內核的充分利用,還能防止過分調度。
線程池(自己總結):線程池就是實現創建若干個可執行的線程放入一個池中,需要的時候從池中獲取,不用自行創建,使用完畢不需要銷燬線程而是放入池中,從而減少了創建和銷燬線程的開銷。
線程池的意義
-
循環利用線程資源,避免重複創建和銷燬線程
-
線程池的任務是異步執行的,只要提交完成就能快速返回,可以提高應用響應性
-
Java線程池還有一個很重要的意義:Java線程池就是JDK 5 推出的Executor框架,在此之前Java線程既是工作任務又是執行機制,而Executor框架把工作任務與執行機制分離開來:工作任務包括Runnable接口和Callable接口,而執行機制由Executor接口提供。
二、 Java中四種線程池
Java中通過工具類Executors提供的4中線程池:
1. newFixedThreadExecutor() : 創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2. newSingleThreadExecutor() :創建單線程化的線程池。這個線程池只有一個線程在工作,如果這個唯一的線程因爲異常停止結束,那麼會有一個新的線程來替代它。該線程池保證所有任務都是按照指定的順序(FIFO,LIFO,優先級)執行 。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,
0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
3. newCachedThreadPool() :創建一個可緩存的線程池,可根據任務數動態調整當前線程數。如果線程池的大小超過了處理任務所需要的線程,那麼就會回收部分空閒(60s不執行)的線程,當任務數增加時, 此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
4. newScheduledThreadPool() : 創建一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
三、線程池的使用
1. ThreadPoolExecutor類
方式一:使用ThreadPoolExecutor(不推薦直接使用)
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5));
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
}
});
方式二:使用Executors類提供的幾個靜態方法(推薦)
ExecutorService executorService1 = Executors.newFixedThreadPool(3);
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
ExecutorService executorService3 = Executors.newCachedThreadPool();
ExecutorService executorService4 = Executors.newScheduledThreadPool(3);
executorService1.execute(new Runnable() {
@Override
public void run() {
}
});
其實ExecutorService纔是真正的線程池接口,所以我們在通過Executors創建各種線程時,都是採用上述代碼所示的方式
四、線程池實現原理
1. 線程池的6個重要參數(需記牢)
-
corePoolSize : 核心池的大小
-
maximumPoolSize : 線程池最大線程數
-
keepAliveTime : 表示線程沒有任務執行時最多保持多久時間會終止。
-
workQueue:一個阻塞隊列,用來存儲等待執行的任務
-
threadFactory:線程工廠,主要用來創建線程;
-
handler:表示當拒絕處理任務時的策略,有以下四種取值
ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
2. 線程池實現原理
當將任務提交給線程池時,即調用execute()方法時,流程如下圖。
a、若當前運行的線程少於corePoolSize,則創建新線程來執行任務(執行這一步需要獲取全局鎖) b、若運行的線程多於或等於corePoolSize,則將任務加入BlockingQueue c、若無法將任務加入BlockingQueue,則創建新的線程來處理任務(執行這一步需要獲取全局鎖) d、若創建新線程將使當前運行的線程超出maximumPoolSize,任務將被拒絕,並調用RejectedExecutionHandler.rejectedExecution() 採取上述思路,是爲了在執行execute()時,儘可能避免獲取全局鎖 在ThreadPoolExecutor完成預熱之後(當前運行的線程數大於等於corePoolSize),幾乎所有的execute()方法調用都是執行步驟b,而步驟b不需要獲取全局鎖
參考
https://baike.baidu.com/item/%E7%BA%BF%E7%A8%8B%E6%B1%A0/4745661
https://www.jianshu.com/p/916ea29ff3b9