這篇文章主要介紹“常見的線程池有哪些”,在日常操作中,相信很多人在常見的線程池有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”常見的線程池有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
成都創(chuàng)新互聯(lián)專注于原州網(wǎng)站建設服務及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供原州營銷型網(wǎng)站建設,原州網(wǎng)站制作、原州網(wǎng)頁設計、原州網(wǎng)站官網(wǎng)定制、微信平臺小程序開發(fā)服務,打造原州網(wǎng)絡公司原創(chuàng)品牌,更為您提供原州網(wǎng)站排名全網(wǎng)營銷落地服務。
FixedThreadPool線程池,它的核心線程數(shù)和最大線程數(shù)是一樣的,所以可以把它看作是固定線程數(shù)的線程池。
特點是:線程池中的線程數(shù)除了初始階段需要從 0 開始增加外,之后的線程數(shù)量就是固定的,就算任務數(shù)超過線程數(shù),線程池也不會再創(chuàng)建更多的線程來處理任務,而是會把超出線程處理能力的任務放到任務隊列中進行等待。而且就算任務隊列滿了,到了本該繼續(xù)增加線程數(shù)的時候,由于它的最大線程數(shù)和核心線程數(shù)是一樣的,所以也無法再增加新的線程了。

如圖所示,線程池有 t0~t9,10 個線程,它們會不停地執(zhí)行任務,如果某個線程任務執(zhí)行完了,就會從任務隊列中獲取新的任務繼續(xù)執(zhí)行,期間線程數(shù)量不會增加也不會減少,始終保持在 10 個。
CachedThreadPool,可以稱作可緩存線程池,它的特點在于線程數(shù)是幾乎可以無限增加的(實際最大可以達到 Integer.MAX_VALUE,為 2^31-1),而當線程閑置時還可以對線程進行回收。
CachedThreadPool 線程池它也有一個用于存儲提交任務的隊列,但這個隊列是 SynchronousQueue,隊列的容量為0,實際不存儲任何任務,它只負責對任務進行中轉(zhuǎn)和傳遞,所以效率比較高。
當提交一個任務后,線程池會判斷已創(chuàng)建的線程中是否有空閑線程,如果有空閑線程則將任務直接指派給空閑線程,如果沒有空閑線程,則新建線程去執(zhí)行任務,這樣就做到了動態(tài)地新增線程。如下方代碼所示。
/**
* @date 2020/11/15 23:17
*
* @discription 緩存線程池
*/
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
service.execute(new ThreadPoolDemo.Task());
}
}
static class Task implements Runnable{
@Override
public void run() {
System.out.println("Thread Name: " + Thread.currentThread().getName());
}
}
}因為執(zhí)行任務簡單,所以在 for 循環(huán)提交任務結(jié)束前,就可能導致先執(zhí)行的任務被執(zhí)行完了,線程空閑出來執(zhí)行后面的任務,從而不會出現(xiàn)線程名稱為pool-1-thread-1000的線程。
假設這些任務處理的時間非常長,因為 for 循環(huán)提交任務的操作是非常快的,但執(zhí)行任務卻比較耗時,就可能導致 1000 個任務都提交完了但第一個任務還沒有被執(zhí)行完,所以此時 CachedThreadPool 就可以動態(tài)的伸縮線程數(shù)量,隨著任務的提交,不停地創(chuàng)建 1000 個線程來執(zhí)行任務,而當任務執(zhí)行完之后,假設沒有新的任務了,那么大量的閑置線程又會造成內(nèi)存資源的浪費,這時線程池就會檢測線程在 60 秒內(nèi)有沒有可執(zhí)行任務,如果沒有就會被銷毀,最終線程數(shù)量會減為 0。
線程池 ScheduledThreadPool,它支持定時或周期性執(zhí)行任務。
比如每隔 10 秒鐘執(zhí)行一次任務,而實現(xiàn)這種功能的方法主要有 3 種,如代碼所示:
/**
* @date 2020/11/15 23:34
*
* @discription 支持定時或周期性執(zhí)行任務的線程池
*/
public class ScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
/**
* schedule —— 比較簡單表示延遲指定時間后執(zhí)行一次任務,
* 如果代碼中設置參數(shù)為 10 秒,也就是 10 秒后執(zhí)行一次任務后就結(jié)束。
*/
service.schedule(new ThreadPoolDemo.Task(),10, TimeUnit.SECONDS);
/**
* scheduleAtFixedRate —— 表示以固定的頻率執(zhí)行任務,
* 它的第二個參數(shù) initialDelay 表示第一次延時時間,
* 第三個參數(shù) period 表示周期也就是第一次延時后每次延時多長時間執(zhí)行一次任務。
*/
service.scheduleAtFixedRate(new ThreadPoolDemo.Task(), 10, 10, TimeUnit.SECONDS);
/**
* scheduleWithFixedDelay —— 與第二種方法類似,也是周期執(zhí)行任務,區(qū)別在于對周期的定義,
* 之前的 scheduleAtFixedRate 是以任務開始的時間為時間起點,
開始計時時間到就開始執(zhí)行第二次任務,而不管任務需要花多久執(zhí)行;
* 而 scheduleWithFixedDelay 方法以任務結(jié)束的時間為下一次循環(huán)的時間起點開始計時。
*/
service.scheduleWithFixedDelay(new ThreadPoolDemo.Task(), 10, 10, TimeUnit.SECONDS);
}
}比如:假設某個同學正在熬夜寫代碼,需要喝咖啡來提神,假設每次喝咖啡都需要花10分鐘的時間,如果此時采用第2種方法 scheduleAtFixedRate,時間間隔設置為 1 小時,那么他將會在每個整點喝一杯咖啡,以下是時間表:
00:00: 開始喝咖啡
00:10: 喝完了
01:00: 開始喝咖啡
01:10: 喝完了
02:00: 開始喝咖啡
02:10: 喝完了
采用第3種方法 scheduleWithFixedDelay,時間間隔同樣設置為 1 小時,那么由于每次喝咖啡需要10分鐘,而 scheduleWithFixedDelay 是以任務完成的時間為時間起點開始計時的,所以第2次喝咖啡的時間將會在1:10,而不是1:00整,以下是時間表:
00:00: 開始喝咖啡
00:10: 喝完了
01:10: 開始喝咖啡
01:20: 喝完了
02:20: 開始喝咖啡
02:30: 喝完了
SingleThreadExecutor,它會使用唯一的線程去執(zhí)行任務,原理和 FixedThreadPool 是一樣的,只不過這里線程只有一個,如果線程在執(zhí)行任務的過程中發(fā)生異常,線程池也會重新創(chuàng)建一個線程來執(zhí)行后續(xù)的任務。
這種線程池由于只有一個線程,所以非常適合用于所有任務都需要按被提交的順序依次執(zhí)行的場景,而前幾種線程池不一定能夠保障任務的執(zhí)行順序等于被提交的順序,因為它們是多線程并行執(zhí)行的。
SingleThreadScheduledExecutor,它實際和第三種 ScheduledThreadPool 線程池非常相似,它只是 ScheduledThreadPool 的一個特例,內(nèi)部只有一個線程。
注意:它只是將 ScheduledThreadPool 的核心線程數(shù)設置為了 1,即new ScheduledThreadPoolExecutor(1),并非最大線程為一的意思。

ForkJoinPool,這個線程池是在 JDK 7 加入的,主要用法和之前的線程池是相同的,也是把任務交給線程池去執(zhí)行,線程池中也有任務隊列來存放任務。ForkJoinPool 非常適合用于遞歸的場景,例如樹的遍歷、最優(yōu)路徑搜索等場景。
ForkJoinPool 線程池和之前的線程池有兩點非常大的不同之處。
比如:我們有一個 Task,這個 Task 可以產(chǎn)生三個子任務,三個子任務并行執(zhí)行完畢后將結(jié)果匯總給 Result,比如說主任務需要執(zhí)行非常繁重的計算任務,我們就可以把計算拆分成三個部分,這三個部分是互不影響相互獨立的,這樣就可以利用 CPU 的多核優(yōu)勢,并行計算,然后將結(jié)果進行匯總。這里面主要涉及兩個步驟,第一步是拆分也就是 Fork,第二步是匯總也就是 Join,這也是ForkJoinPool 線程池名字的由來。
ForkJoinPool 線程池有多種方法可以實現(xiàn)任務的分裂和匯總,其中一種用法計算斐波那契數(shù)列如下方代碼所示。
/**
* @date 2020/11/16 0:06
*
* @discription ForkJoinPool計算斐波那契數(shù)列
* 打印出斐波那契數(shù)列的第 0 到 9 項的值:
*/
public class Fibonacci extends RecursiveTask<Integer> {
int n;
public Fibonacci(int n) {
this.n = n;
}
@Override
protected Integer compute() {
if(n <= 1){
return n;
}
Fibonacci f1 = new Fibonacci(n-1);
f1.fork();
Fibonacci f2 = new Fibonacci(n-2);
f2.fork();
return f1.join() + f2.join();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ForkJoinPool forkJoinPool = new ForkJoinPool();
for (int i = 0; i < 10; i++) {
ForkJoinTask task = forkJoinPool.submit(new Fibonacci(i));
System.out.println(task.get());
}
}
}它首先繼承了 RecursiveTask,RecursiveTask 類是對ForkJoinTask 的一個簡單的包裝,這時我們重寫 compute() 方法,當 n<=1 時直接返回,當 n>1 就創(chuàng)建遞歸任務,也就是 f1 和 f2,然后我們用 fork() 方法分裂任務并分別執(zhí)行,最后在 return 的時候,使用 join() 方法把結(jié)果匯總,這樣就實現(xiàn)了任務的分裂和匯總。
之前的線程池所有的線程共用一個隊列,但 ForkJoinPool 線程池中每個線程都有自己獨立的任務隊列,如圖所示。

可以看到 ForkJoinPool 線程池和其他線程池很多地方都是一樣的,但重點區(qū)別在于除了有一個共用的任務隊列之外,它每個線程都有一個自己的雙端隊列來存儲分裂出來的子任務。
當線程中的任務被 Fork 分裂了,分裂出來的子任務放入線程自己的 deque 里,而不是放入公共的任務隊列中。對于當前線程來說以直接在自己的任務隊列中獲取,而不必去公共隊列中爭搶,也不會發(fā)生阻塞(除了 steal 情況外),減少了線程間的競爭和切換,是非常高效的。
若此時線程有多個,而線程 t1 的任務特別繁重,分裂了數(shù)十個子任務,但是 t0 此時卻無事可做,它自己的 deque 隊列為空,這時為了提高效率,t0 就會想辦法幫助 t1 執(zhí)行任務,這就是“work-stealing”的含義。雙端隊列 deque 中,線程 t1 獲取任務的邏輯是后進先出,而線程 t0 在“steal”偷線程 t1 的 deque 中的任務的邏輯是先進先出,使用 “work-stealing” 算法和雙端隊列很好地平衡了各線程的負載。
到此,關(guān)于“常見的線程池有哪些”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
文章名稱:常見的線程池有哪些
標題來源:http://www.yijiale78.com/article22/gippjc.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、Google、營銷型網(wǎng)站建設、服務器托管、做網(wǎng)站、靜態(tài)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)