目錄介紹
- 1.ThreadPoolExecutor類介紹
- 1.1 構造函數
- 1.2 參數解析
- 1.3 遵循的規則
- 1.4 使用線程池管理線程的優點
- 2.關于線程池的分類
- 2.1 FixedThreadPool
- 2.2 CachedThreadPool
- 2.3 ScheduledThreadPool
- 2.4 SingleThreadExecutor
- 3.線程池一般用法
- 3.1 一般方法介紹
- 3.2 newFixedThreadPool的使用
- 3.3 newSingleThreadExecutor的使用
- 3.4 newCachedThreadPool的使用
- 3.5 newScheduledThreadPool的使用
- 3.6 線程創建規則
- 4.線程池封裝
- 4.1 具體可以參考下篇文章
- 4.2 參考博客
好消息
- 博客筆記大匯總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug匯總,當然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計47篇[近20萬字],轉載請注明出處,謝謝!
- 鏈接地址:https://github.com/yangchong211/YCBlogs
- 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起于忽微,量變引起質變!
前言介紹
- 關于線程池關聯博客有:
- 多線程1,線程基礎知識
- 多線程2,線程池深入理解
- 多線程3,線程池封裝庫
- 如果覺得前兩篇線程知識太基礎,可以直接忽略……主要是回顧基礎知識點!
1.ThreadPoolExecutor類介紹
1.1 構造函數
- ExecutorService是最初的線程池接口,ThreadPoolExecutor類是對線程池的具體實現,它通過構造方法來配置線程池的參數
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
1.2 參數解析
- corePoolSize,線程池中核心線程的數量,默認情況下,即使核心線程沒有任務在執行它也存在的,我們固定一定數量的核心線程且它一直存活這樣就避免了一般情況下CPU創建和銷毀線程帶來的開銷。我們如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置為true,那么閑置的核心線程就會有超時策略,這個時間由keepAliveTime來設定,即keepAliveTime時間內如果核心線程沒有回應則該線程就會被終止。allowCoreThreadTimeOut默認為false,核心線程沒有超時時間。
- maximumPoolSize,線程池中的最大線程數,當任務數量超過最大線程數時其它任務可能就會被阻塞。最大線程數=核心線程+非核心線程。非核心線程只有當核心線程不夠用且線程池有空余時才會被創建,執行完任務后非核心線程會被銷毀。
- keepAliveTime,非核心線程的超時時長,當執行時間超過這個時間時,非核心線程就會被回收。當allowCoreThreadTimeOut設置為true時,此屬性也作用在核心線程上。
- unit,枚舉時間單位,TimeUnit。
- workQueue,線程池中的任務隊列,我們提交給線程池的runnable會被存儲在這個對象上。
1.3 遵循的規則
- 當線程池中的核心線程數量未達到最大線程數時,啟動一個核心線程去執行任務;
- 如果線程池中的核心線程數量達到最大線程數時,那么任務會被插入到任務隊列中排隊等待執行;
- 如果在上一步驟中任務隊列已滿但是線程池中線程數量未達到限定線程總數,那么啟動一個非核心線程來處理任務;
- 如果上一步驟中線程數量達到了限定線程總量,那么線程池則拒絕執行該任務,且ThreadPoolExecutor會調用RejectedtionHandler的rejectedExecution方法來通知調用者。
1.4 使用線程池管理線程的優點
- 1、線程的創建和銷毀由線程池維護,一個線程在完成任務后并不會立即銷毀,而是由后續的任務復用這個線程,從而減少線程的創建和銷毀,節約系統的開銷
- 2、線程池旨在線程的復用,這就可以節約我們用以往的方式創建線程和銷毀所消耗的時間,減少線程頻繁調度的開銷,從而節約系統資源,提高系統吞吐量
- 3、在執行大量異步任務時提高了性能
- 4、Java內置的一套ExecutorService線程池相關的api,可以更方便的控制線程的最大并發數、線程的定時任務、單線程的順序執行等
2.關于線程池的分類
2.1 FixedThreadPool
- 通過Executors的newFixedThreadPool()方法創建,它是個線程數量固定的線程池,該線程池的線程全部為核心線程,它們沒有超時機制且排隊任務隊列無限制,因為全都是核心線程,所以響應較快,且不用擔心線程會被回收。
2.2 CachedThreadPool
- 通過Executors的newCachedThreadPool()方法來創建,它是一個數量無限多的線程池,它所有的線程都是非核心線程,當有新任務來時如果沒有空閑的線程則直接創建新的線程不會去排隊而直接執行,并且超時時間都是60s,所以此線程池適合執行大量耗時小的任務。由于設置了超時時間為60s,所以當線程空閑一定時間時就會被系統回收,所以理論上該線程池不會有占用系統資源的無用線程。
2.3 ScheduledThreadPool
- 通過Executors的newScheduledThreadPool()方法來創建,ScheduledThreadPool線程池像是上兩種的合體,它有數量固定的核心線程,且有數量無限多的非核心線程,但是它的非核心線程超時時間是0s,所以非核心線程一旦空閑立馬就會被回收。這類線程池適合用于執行定時任務和固定周期的重復任務。
2.4 SingleThreadExecutor
- 通過Executors的newSingleThreadExecutor()方法來創建,它內部只有一個核心線程,它確保所有任務進來都要排隊按順序執行。它的意義在于,統一所有的外界任務到同一線程中,讓調用者可以忽略線程同步問題。
3.線程池一般用法
3.1 一般方法介紹
- shutDown(),關閉線程池,需要執行完已提交的任務;
- shutDownNow(),關閉線程池,并嘗試結束已提交的任務;
- allowCoreThreadTimeOut(boolen),允許核心線程閑置超時回收;
- execute(),提交任務無返回值;
- submit(),提交任務有返回值;
3.2 newFixedThreadPool的使用
- 3.2.1 創建一個newFixedThreadPool線程池
private void newFixedThreadPool() {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
for (int i = 1; i <= 20; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
Log.e("瀟湘劍雨", "線程:"+threadName+",正在執行第" + index + "個任務");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
- 3.2.2 打印日志如下
- 創建了一個線程數為5的固定線程數量的線程池,同理該線程池支持的線程最大并發數也是5,模擬20個任務讓它處理,執行任務。最后我們獲取線程的信息,打印日志。
-
日志如圖所示:
image
3.3 newSingleThreadExecutor的使用
- 3.3.1 創建一個newSingleThreadExecutor線程池
private void newSingleThreadExecutor() {
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
for (int i = 1; i <= number; i++) {
final int index = i;
singleThreadPool.execute(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
Log.v("瀟湘劍雨", "線程:"+threadName+",正在執行第" + index + "個任務");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
- 3.3.2 打印日志如下
-
改了線程池的實現方式,即依次一個一個的處理任務,而且都是復用一個線程,日志為
image
3.4 newCachedThreadPool的使用
- 3.4.1 創建一個newCachedThreadPool線程池
private void newCachedThreadPool() {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 1; i <= number; i++) {
final int index = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
Log.v("瀟湘劍雨newCachedThreadPool", "線程:" + threadName + ",正在執行第" + index + "個任務");
try {
long time = index * 500;
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
- 3.4.2 打印日志如下
-
為了體現該線程池可以自動根據實現情況進行線程的重用,而不是一味的創建新的線程去處理任務,我設置了每隔1s去提交一個新任務,這個新任務執行的時間也是動態變化的,所以,效果為:
image
3.5 newScheduledThreadPool的使用
- 3.5.1 創建一個newScheduledThreadPool線程池
private void newScheduledThreadPool() {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//延遲2秒后執行該任務
scheduledThreadPool.schedule(new Runnable() {
@SuppressLint("LongLogTag")
@Override
public void run() {
String threadName = Thread.currentThread().getName();
Log.e("瀟湘劍雨newScheduledThreadPool", "線程:" + threadName + ",正在執行");
}
}, 2, TimeUnit.SECONDS);
//延遲1秒后,每隔2秒執行一次該任務
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
Log.e("瀟湘劍雨", "線程:" + threadName + ",正在執行");
}
}, 1, 2, TimeUnit.SECONDS);
}
- 3.5.2 打印日志如下
-
通過日志可以發現schedule方法的任務只是執行了一次,然后每隔2秒執行一次該scheduleAtFixedRate方法中的任務
image
3.6 線程創建規則
- ThreadPoolExecutor對象初始化時,不創建任何執行線程,當有新任務進來時,才會創建執行線程。構造ThreadPoolExecutor對象時,需要配置該對象的核心線程池大小和最大線程池大小
- 當目前執行線程的總數小于核心線程大小時,所有新加入的任務,都在新線程中處理。
- 當目前執行線程的總數大于或等于核心線程時,所有新加入的任務,都放入任務緩存隊列中。
- 當目前執行線程的總數大于或等于核心線程,并且緩存隊列已滿,同時此時線程總數小于線程池的最大大小,那么創建新線程,加入線程池中,協助處理新的任務。
- 當所有線程都在執行,線程池大小已經達到上限,并且緩存隊列已滿時,就rejectHandler拒絕新的任務。
4.線程池封裝
4.1 具體可以參考下篇文章
4.2 參考博客
- Android性能優化之使用線程池處理異步任務:https://blog.csdn.net/u010687392/article/details/49850803