90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

我記得之前在面試的時候,面試官問我單線程池有什么意義?我跟面試官說:雖然是單線程池,但提供了工作隊(duì)列,生命周期管理,工作線程維護(hù)等功能。

雖然有點(diǎn)籠統(tǒng),但是誰又能說我說的有錯呢,單線程,無論是在面試得過程中還是日常開發(fā),都算是一個很重要的知識點(diǎn),今天沒什么事情,我就結(jié)合源碼+手寫得案例,帶大家看一下線程吃的7種創(chuàng)建方式

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

文章首發(fā)公眾號:Java架構(gòu)師聯(lián)盟,每日更新技術(shù)好文

1.FixedThreadPool

創(chuàng)建一個固定大小的線程池,可控制并發(fā)的線程數(shù),超出的線程會在隊(duì)列中等待。

源碼

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

使用示例如下:

package com.test.thread;
?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
?
/**
 * @author :biws
 * @date :Created in 2020/12/17 19:23
 * @description:測試FixedThreadPool
 */
public class testThread1 {
 public static void fixedThreadPool() {
 // 創(chuàng)建 2 個數(shù)據(jù)級的線程池
 ExecutorService threadPool = Executors.newFixedThreadPool(2);
?
 // 創(chuàng)建任務(wù)
 Runnable runnable = new Runnable() {
 @Override
 public void run() {
 System.out.println("任務(wù)被執(zhí)行,線程:" + Thread.currentThread().getName());
 }
 };
?
 // 線程池執(zhí)行任務(wù)(一次添加 4 個任務(wù))
 // 執(zhí)行任務(wù)的方法有兩種:submit 和 execute
 threadPool.submit(runnable);  // 執(zhí)行方式 1:submit
 threadPool.execute(runnable); // 執(zhí)行方式 2:execute
 threadPool.execute(runnable);
 threadPool.execute(runnable);
?
?
 }
?
 public static void main(String[] args) {
 fixedThreadPool();
 }
}

執(zhí)行結(jié)果如下:

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

后來我將測試數(shù)量提交到100,而線程池中處理線程得數(shù)量增加到4

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

執(zhí)行結(jié)果

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

最開始,提交4個線程執(zhí)行,之后的線程會在隊(duì)列中排序等待被執(zhí)行

如果覺得以上方法比較繁瑣,還可以用更簡單的使用方法,如下代碼所示:

package com.test.thread;
?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
?
/**
 * @author :biws
 * @date :Created in 2020/12/17 19:27
 * @description:FixedThreadPool進(jìn)階寫法
 */
public class testThreadG {
 public static void fixedThreadPool() {
 // 創(chuàng)建線程池
 ExecutorService threadPool = Executors.newFixedThreadPool(2);
 /*
 * 執(zhí)行任務(wù)
 * 為了個之前得進(jìn)行統(tǒng)一,所以這里通過for循環(huán),同樣是提交4個執(zhí)行*/
 for(int i=1;i<=4;i++) {
 threadPool.execute(() -> {
 System.out.println("任務(wù)被執(zhí)行,線程:" + Thread.currentThread().getName());
 });
 }
 }
?
 public static void main(String[] args) {
 fixedThreadPool();
 }
}

2.CachedThreadPool

創(chuàng)建一個可緩存的線程池,若線程數(shù)超過處理所需,緩存一段時間后會回收,若線程數(shù)不夠,則新建線程。

源碼

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

參數(shù)詳解:

corePoolSize = 0,

maximumPoolSize設(shè)置為Integer.MAX_VALUE,代表沒有核心線程,非核心線程是無界的;keepAliveTime = 60L,空閑線程等待新任務(wù)的最長時間是60s;

用了阻塞隊(duì)列SynchronousQueue,是一個不存儲元素的阻塞隊(duì)列

使用示例如下:

package com.test.thread;
?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
?
/**
 * @author :biws
 * @date :Created in 2020/12/17 19:38
 * @description:測試CachedThreadPool
 */
public class testThread2 {
 public static void cachedThreadPool() {
 // 創(chuàng)建線程池
 ExecutorService threadPool = Executors.newCachedThreadPool();
 // 執(zhí)行任務(wù)
 for (int i = 0; i < 10; i++) {
 threadPool.execute(() -> {
 System.out.println("任務(wù)被執(zhí)行,線程:" + Thread.currentThread().getName());
 try {
 TimeUnit.SECONDS.sleep(1);
 } catch (InterruptedException e) {
 }
 });
 }
 }
?
 public static void main(String[] args) {
 cachedThreadPool();
 }
}
}

執(zhí)行結(jié)果如下:

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

從上述結(jié)果可以看出,線程池創(chuàng)建了 10 個線程來執(zhí)行相應(yīng)的任務(wù)。

而如果我將sleep注釋之后,再來看執(zhí)行結(jié)果

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

我想這個結(jié)果一目了然吧,大家如果可以的話,可以把這個執(zhí)行數(shù)據(jù)提升一下,然后再查看一下結(jié)果應(yīng)該會更明顯

3.SingleThreadExecutor

創(chuàng)建單個線程數(shù)的線程池,它可以保證先進(jìn)先出的執(zhí)行順序。為了能讓大家看的更清楚,所以每個線程在執(zhí)行得時候我都添加了他的時間

使用示例如下:

package com.test.thread;
?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
?
/**
 * @author :biws
 * @date :Created in 2020/12/17 20:17
 * @description:測試singleThreadExecutor
 */
public class testThread4 {
?
 public static void singleThreadExecutor() {
 // 創(chuàng)建線程池
 ExecutorService threadPool = Executors.newSingleThreadExecutor();
 // 執(zhí)行任務(wù)
 for (int i = 0; i < 10; i++) {
 final int index = i;
?
 threadPool.execute(() -> {
 new Thread();
 System.out.println(Thread.currentThread().getName()+"開始時間[" + new java.util.Date().getTime());
 System.out.println(index + ":任務(wù)被執(zhí)行");
?
 try {
 TimeUnit.SECONDS.sleep(1);
 } catch (InterruptedException e) {
 }
 System.out.println(Thread.currentThread().getName()+"結(jié)束時間[" + new java.util.Date().getTime());
 });
 }
 }
?
 public static void main(String[] args) {
 singleThreadExecutor();
?
 }
}

執(zhí)行結(jié)果如下:

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

4.ScheduledThreadPool

創(chuàng)建一個可以執(zhí)行延遲任務(wù)的線程池。

源碼

這個線程得源碼有一點(diǎn)特殊,由兩部分組成,第一部分

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

這里創(chuàng)建了ScheduledThreadPoolExecutor,ScheduledThreadPoolExecutor繼承自ThreadPoolExecutor,主要用于給定 延時之后的運(yùn)行任務(wù)或定期處理任務(wù)

第二部分

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

使用示例如下:

package com.test.thread;
?
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
?
/**
 * @author :biws
 * @date :Created in 2020/12/17 20:43
 * @description:測試ScheduledThreadPool
 */
public class testThread3 {
 public static void scheduledThreadPool() {
 // 創(chuàng)建線程池
 ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
 // 添加定時執(zhí)行任務(wù)(1s 后執(zhí)行)
 for(int i=0;i<4;i++){
 final int index=i;
?
 System.out.println(index+"添加任務(wù),時間:" + new Date()+Thread.currentThread().getName());
 threadPool.schedule(() -> {
 System.out.println(index+"任務(wù)被執(zhí)行,時間:" + new Date()+Thread.currentThread().getName());
 /* try {
 TimeUnit.SECONDS.sleep(1);
 } catch (InterruptedException e) {
 }*/
 }, 1, TimeUnit.SECONDS);
 }
 }
 public static void main(String[] args) {
 scheduledThreadPool();
 }
}

執(zhí)行結(jié)果如下:

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

5.SingleThreadScheduledExecutor

創(chuàng)建一個單線程的可以執(zhí)行延遲任務(wù)的線程池。

使用示例如下:

package com.test.thread;
?
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
?
/**
 * @author :biws
 * @date :Created in 2020/12/17 20:54
 * @description:測試SingleThreadScheduledExecutor
 */
public class testThread5 {
 public static void SingleThreadScheduledExecutor() {
 // 創(chuàng)建線程池
 ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
 // 添加定時執(zhí)行任務(wù)(2s 后執(zhí)行)
 for (int i=0;i<4;i++) {
?
 final  int index=i;
 System.out.println(index+"添加任務(wù),時間:" + new Date());
 threadPool.schedule(() -> {
 System.out.println(index+"任務(wù)被執(zhí)行,時間:" + new Date());
 try {
 TimeUnit.SECONDS.sleep(1);
 } catch (InterruptedException e) {
 }
 }, 2, TimeUnit.SECONDS);
 }
 }
?
 public static void main(String[] args) {
 SingleThreadScheduledExecutor();
 }
}

執(zhí)行結(jié)果如下:

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

從上述結(jié)果可以看出,任務(wù)在 2 秒之后被執(zhí)行了,符合我們的預(yù)期。

6.newWorkStealingPool

創(chuàng)建一個搶占式執(zhí)行的線程池(任務(wù)執(zhí)行順序不確定)

注意:

此方法只有在 JDK 1.8+ 版本中才能使用。

使用示例如下:

public static void workStealingPool() {
 // 創(chuàng)建線程池
 ExecutorService threadPool = Executors.newWorkStealingPool();
 // 執(zhí)行任務(wù)
 for (int i = 0; i < 10; i++) {
 final int index = i;
 threadPool.execute(() -> {
 System.out.println(index + " 被執(zhí)行,線程名:" + Thread.currentThread().getName());
 });
 }
 // 確保任務(wù)執(zhí)行完成
 while (!threadPool.isTerminated()) {
 }
}

執(zhí)行結(jié)果如下:

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

7.ThreadPoolExecutor

最原始的創(chuàng)建線程池的方式,它包含了 7 個參數(shù)可供設(shè)置。

使用示例如下:

package com.test.thread;
?
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
?
/**
 * @author :biws
 * @date :Created in 2020/12/17 21:07
 * @description:測試ThreadPoolExecutor
 */
public class testThread7 {
 public static void myThreadPoolExecutor() {
 // 創(chuàng)建線程池
 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(

 TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
 // 執(zhí)行任務(wù)
 for (int i = 0; i < 10; i++) {
 final int index = i;
 threadPool.execute(() -> {
 System.out.println(index + " 被執(zhí)行,線程名:" + Thread.currentThread().getName());
 try {
 Thread.sleep(1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 });
 }
 }
?
 public static void main(String[] args) {
 myThreadPoolExecutor();
 }
}

執(zhí)行結(jié)果如下:

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

ThreadPoolExecutor 參數(shù)介紹

就像我前面説的,ThreadPoolExecutor 是可以設(shè)置一些參數(shù)的,在我的代碼中,我只設(shè)置了這幾個參數(shù)

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

源碼中是這樣編寫的

    public ThreadPoolExecutor(
                                                    int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

7 個參數(shù)代表的含義如下:

corePoolSize

核心線程數(shù),線程池中始終存活的線程數(shù)。

maximumPoolSize

最大線程數(shù),線程池中允許的最大線程數(shù),當(dāng)線程池的任務(wù)隊(duì)列滿了之后可以創(chuàng)建的最大線程數(shù)。

keepAliveTime \ unit:

最大線程數(shù)可以存活的時間,當(dāng)線程中沒有任務(wù)執(zhí)行時,最大線程就會銷毀一部分,最終保持核心線程數(shù)量的線程。

單位是和參數(shù) 3 存活時間配合使用的,合在一起用于設(shè)定線程的存活時間

threadFactory

線程工廠,主要用來創(chuàng)建線程,默認(rèn)為正常優(yōu)先級、非守護(hù)線程。

workQueue

一個阻塞隊(duì)列,用來存儲線程池等待執(zhí)行的任務(wù),均為線程安全,它包含以下 7 種類型:

ArrayBlockingQueue:一個由數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列。

LinkedBlockingQueue:一個由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列。

SynchronousQueue:一個不存儲元素的阻塞隊(duì)列,即直接提交給線程不保持它們。

PriorityBlockingQueue:一個支持優(yōu)先級排序的無界阻塞隊(duì)列。

DelayQueue:一個使用優(yōu)先級隊(duì)列實(shí)現(xiàn)的無界阻塞隊(duì)列,只有在延遲期滿時才能從中提取元素。

LinkedTransferQueue:一個由鏈表結(jié)構(gòu)組成的無界阻塞隊(duì)列。與SynchronousQueue類似,還含有非阻塞方法。

LinkedBlockingDeque:一個由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列。

較常用的是 LinkedBlockingQueue 和 Synchronous,線程池的排隊(duì)策略與 BlockingQueue 有關(guān)。

handler

拒絕策略,拒絕處理任務(wù)時的策略,系統(tǒng)提供了 4 種可選:

默認(rèn)策略為 AbortPolicy。

代碼演示

。。。。。
        // 創(chuàng)建線程,線程的任務(wù)隊(duì)列的長度為 1
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
                100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),
                new ThreadPoolExecutor.DiscardPolicy());
。。。。

//在Java種共有4種拒絕策略
/*
AbortPolicy:拒絕并拋出異常。

CallerRunsPolicy:使用當(dāng)前調(diào)用的線程來執(zhí)行此任務(wù)。

DiscardOldestPolicy:拋棄隊(duì)列頭部(最舊)的一個任務(wù),并執(zhí)行當(dāng)前任務(wù)。

DiscardPolicy:忽略并拋棄當(dāng)前任務(wù)。
*/</pre>

演示結(jié)果

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

我們創(chuàng)建了一個核心線程數(shù)和最大線程數(shù)都為 1 的線程池,并且給線程池的任務(wù)隊(duì)列設(shè)置為 1,這樣當(dāng)我們有 2 個以上的任務(wù)時就會觸發(fā)拒絕策略,

自定義拒絕策略

除了 Java 自身提供的 4 種拒絕策略之外,我們也可以自定義拒絕策略,示例代碼如下:

。。。

        // 創(chuàng)建線程,線程的任務(wù)隊(duì)列的長度為 1
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
                100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        // 執(zhí)行自定義拒絕策略的相關(guān)操作
                        System.out.println("我是自定義拒絕策略~");
                    }
                });

。。。

程序的執(zhí)行結(jié)果如下:

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

線程池的執(zhí)行流程

提交一個任務(wù)到線程池中,線程池的處理流程如下:

1、判斷線程池里的核心線程是否都在執(zhí)行任務(wù),如果不是(核心線程空閑或者還有核心線程沒有被創(chuàng)建)則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù)。如果核心線程都在執(zhí)行任務(wù),則進(jìn)入下個流程。

2、線程池判斷工作隊(duì)列是否已滿,如果工作隊(duì)列沒有滿,則將新提交的任務(wù)存儲在這個工作隊(duì)列里。如果工作隊(duì)列滿了,則進(jìn)入下個流程。

3、判斷線程池里的線程是否都處于工作狀態(tài),如果沒有,則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù)。如果已經(jīng)滿了,則交給飽和策略來處理這個任務(wù)。

90分鐘10個手寫案例,從源碼底層給你講解7種線程池創(chuàng)建方式

總結(jié)

今天,我花費(fèi)90分鐘得時間,一點(diǎn)點(diǎn)得手敲案例,將7個線程池的創(chuàng)建方式進(jìn)行講解,但是在日常得生活中,我應(yīng)用最多的就是hreadPoolExecutor,因?yàn)樗膮?shù)以及拒絕策略有時會讓開發(fā)過程更加可控一些,并且支持自定義,這就很6

希望本文的內(nèi)容能幫助到你。原創(chuàng)不易,覺得不錯就點(diǎn)個贊再走吧!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容