中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

常見的線程池有哪些

發布時間:2021-10-25 09:22:29 來源:億速云 閱讀:163 作者:iii 欄目:編程語言

這篇文章主要介紹“常見的線程池有哪些”,在日常操作中,相信很多人在常見的線程池有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”常見的線程池有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

一:幾種常見線程池

1.1 FixedThreadPool

FixedThreadPool線程池,它的核心線程數和最大線程數是一樣的,所以可以把它看作是固定線程數的線程池。

特點是:線程池中的線程數除了初始階段需要從 0 開始增加外,之后的線程數量就是固定的,就算任務數超過線程數,線程池也不會再創建更多的線程來處理任務,而是會把超出線程處理能力的任務放到任務隊列中進行等待。而且就算任務隊列滿了,到了本該繼續增加線程數的時候,由于它的最大線程數和核心線程數是一樣的,所以也無法再增加新的線程了。

常見的線程池有哪些

如圖所示,線程池有 t0~t9,10 個線程,它們會不停地執行任務,如果某個線程任務執行完了,就會從任務隊列中獲取新的任務繼續執行,期間線程數量不會增加也不會減少,始終保持在 10 個。

1.2 CachedThreadPool

CachedThreadPool,可以稱作可緩存線程池,它的特點在于線程數是幾乎可以無限增加的(實際最大可以達到 Integer.MAX_VALUE,為 2^31-1),而當線程閑置時還可以對線程進行回收。

CachedThreadPool 線程池它也有一個用于存儲提交任務的隊列,但這個隊列是 SynchronousQueue,隊列的容量為0,實際不存儲任何任務,它只負責對任務進行中轉和傳遞,所以效率比較高

當提交一個任務后,線程池會判斷已創建的線程中是否有空閑線程,如果有空閑線程則將任務直接指派給空閑線程,如果沒有空閑線程,則新建線程去執行任務,這樣就做到了動態地新增線程。如下方代碼所示。

/**
 * @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());
        }
    }
}

因為執行任務簡單,所以在 for 循環提交任務結束前,就可能導致先執行的任務被執行完了,線程空閑出來執行后面的任務,從而不會出現線程名稱為pool-1-thread-1000的線程。

假設這些任務處理的時間非常長,因為 for 循環提交任務的操作是非常快的,但執行任務卻比較耗時,就可能導致 1000 個任務都提交完了但第一個任務還沒有被執行完,所以此時 CachedThreadPool 就可以動態的伸縮線程數量,隨著任務的提交,不停地創建 1000 個線程來執行任務,而當任務執行完之后,假設沒有新的任務了,那么大量的閑置線程又會造成內存資源的浪費,這時線程池就會檢測線程在 60 秒內有沒有可執行任務,如果沒有就會被銷毀,最終線程數量會減為 0。

1.3 ScheduledThreadPool

線程池 ScheduledThreadPool,它支持定時或周期性執行任務。

比如每隔 10 秒鐘執行一次任務,而實現這種功能的方法主要有 3 種,如代碼所示:

/**
 * @date 2020/11/15 23:34
 *
 * @discription 支持定時或周期性執行任務的線程池
 */
public class ScheduledThreadPool {

    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

        /**
         * schedule —— 比較簡單表示延遲指定時間后執行一次任務,
         * 如果代碼中設置參數為 10 秒,也就是 10 秒后執行一次任務后就結束。
         */
        service.schedule(new ThreadPoolDemo.Task(),10, TimeUnit.SECONDS);

        /**
         * scheduleAtFixedRate —— 表示以固定的頻率執行任務,
         * 它的第二個參數 initialDelay 表示第一次延時時間,
         * 第三個參數 period 表示周期也就是第一次延時后每次延時多長時間執行一次任務。
         */
        service.scheduleAtFixedRate(new ThreadPoolDemo.Task(), 10, 10, TimeUnit.SECONDS);

        /**
         * scheduleWithFixedDelay —— 與第二種方法類似,也是周期執行任務,區別在于對周期的定義,
         * 之前的 scheduleAtFixedRate 是以任務開始的時間為時間起點,
		 開始計時時間到就開始執行第二次任務,而不管任務需要花多久執行;
         * 而 scheduleWithFixedDelay 方法以任務結束的時間為下一次循環的時間起點開始計時。
         */
        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: 喝完了

1.4 SingleThreadExecutor

SingleThreadExecutor,它會使用唯一的線程去執行任務,原理和 FixedThreadPool 是一樣的,只不過這里線程只有一個,如果線程在執行任務的過程中發生異常,線程池也會重新創建一個線程來執行后續的任務。

這種線程池由于只有一個線程,所以非常適合用于所有任務都需要按被提交的順序依次執行的場景,而前幾種線程池不一定能夠保障任務的執行順序等于被提交的順序,因為它們是多線程并行執行的。

1.5 SingleThreadScheduledExecutor

SingleThreadScheduledExecutor,它實際和第三種 ScheduledThreadPool 線程池非常相似,它只是 ScheduledThreadPool 的一個特例,內部只有一個線程。

注意:它只是將 ScheduledThreadPool 的核心線程數設置為了 1,即new ScheduledThreadPoolExecutor(1),并非最大線程為一的意思。

1.6 總結

常見的線程池有哪些

二:JDK1.7 新增新增線程池 ForkJoinPool

ForkJoinPool,這個線程池是在 JDK 7 加入的,主要用法和之前的線程池是相同的,也是把任務交給線程池去執行,線程池中也有任務隊列來存放任務。ForkJoinPool 非常適合用于遞歸的場景,例如樹的遍歷、最優路徑搜索等場景。

ForkJoinPool 線程池和之前的線程池有兩點非常大的不同之處。

第一點它非常適合執行可以產生子任務的任務

比如:我們有一個 Task,這個 Task 可以產生三個子任務,三個子任務并行執行完畢后將結果匯總給 Result,比如說主任務需要執行非常繁重的計算任務,我們就可以把計算拆分成三個部分,這三個部分是互不影響相互獨立的,這樣就可以利用 CPU 的多核優勢,并行計算,然后將結果進行匯總。這里面主要涉及兩個步驟,第一步是拆分也就是 Fork,第二步是匯總也就是 Join,這也是ForkJoinPool 線程池名字的由來。

ForkJoinPool 線程池有多種方法可以實現任務的分裂和匯總,其中一種用法計算斐波那契數列如下方代碼所示。

/**
 * @date 2020/11/16 0:06
 *
 * @discription ForkJoinPool計算斐波那契數列
 *              打印出斐波那契數列的第 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 就創建遞歸任務,也就是 f1 和 f2,然后我們用 fork() 方法分裂任務并分別執行,最后在 return 的時候,使用 join() 方法把結果匯總,這樣就實現了任務的分裂和匯總。

第二點不同之處在于內部結構

之前的線程池所有的線程共用一個隊列,但 ForkJoinPool 線程池中每個線程都有自己獨立的任務隊列,如圖所示。

常見的線程池有哪些

可以看到 ForkJoinPool 線程池和其他線程池很多地方都是一樣的,但重點區別在于除了有一個共用的任務隊列之外,它每個線程都有一個自己的雙端隊列來存儲分裂出來的子任務。

  • 當線程中的任務被 Fork 分裂了,分裂出來的子任務放入線程自己的 deque 里,而不是放入公共的任務隊列中。對于當前線程來說以直接在自己的任務隊列中獲取,而不必去公共隊列中爭搶,也不會發生阻塞(除了 steal 情況外),減少了線程間的競爭和切換,是非常高效的。

  • 若此時線程有多個,而線程 t1 的任務特別繁重,分裂了數十個子任務,但是 t0 此時卻無事可做,它自己的 deque 隊列為空,這時為了提高效率,t0 就會想辦法幫助 t1 執行任務,這就是“work-stealing”的含義。雙端隊列 deque 中,線程 t1 獲取任務的邏輯是后進先出,而線程 t0 在“steal”偷線程 t1 的 deque 中的任務的邏輯是先進先出使用 “work-stealing” 算法和雙端隊列很好地平衡了各線程的負載

到此,關于“常見的線程池有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

扎鲁特旗| 潢川县| 拉萨市| 十堰市| 南充市| 东乌| 盘山县| 荃湾区| 苏尼特右旗| 长泰县| 忻城县| 遂昌县| 天峻县| 竹北市| 冕宁县| 阳曲县| 铁岭县| 澎湖县| 儋州市| 苏尼特左旗| 嵊泗县| 五指山市| 陈巴尔虎旗| 大竹县| 马尔康县| 交城县| 桃江县| 瑞金市| 岗巴县| 桃园县| 盖州市| 丰镇市| 北川| 洛川县| 文登市| 凤冈县| 鹤峰县| 苍梧县| 临高县| 师宗县| 垫江县|