大話線程池原理

1.前言

  一開始很猶豫這要不要寫這篇文章,在網上看了不少文章寫的都很不錯,可是秉持着更全更易懂的原則,仍是打算本身整理一篇。也參考了不少的文章博客,但願這篇文章可以真正的幫到你。(同時吐槽下,稀土掘金就不能增長一個分類專區的功能嗎,這樣博客寫多了,很差歸類的)java

2.爲毛用它

  1. 下降資源消耗:經過它重用已存在的線程,能夠有效的下降,由頻繁建立和銷燬線程所形成資源的消耗。
  2. 增長系統穩定性:由線程池統一管理,除了能夠下降資源的消耗,還能夠有效控制線程的併發數量,不會讓線程無限制的建立,增長了系統的穩定性。
  3. 提升了響應速度:由於重用線程,因此提升了響應速度。

3.線程池介紹

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 複製代碼

1. corePoolSize(最大核心線程數):

  線程池建立線程的時候,若是當前的線程總個數 < corePoolSize,那麼新建的線程爲核心線程,若是當前線程總個數 >= corePoolSize,那麼新建的線程爲非核心線程。   核心線程默認會一直存活下去,即使是空閒狀態,可是若是設置了allowCoreThreadTimeOut(true)的話,那麼核心線程空閒時間達到keepAliveTime也將關閉。緩存

2. maximumPoolSize(線程池能容納的最大線程數量):

  核心線程數 + 非核心線程數 = 最大線程數量微信

3. keepAliveTime(線程的存活時間):

  非核心線程在空閒狀態下,超過keepAliveTime時間,就會被回收,若是核心線程設置了allowCoreThreadTimeOut(true)的話,那麼在空閒時,超過keepAliveTime時間,也會被回收。併發

4. TimeUnit(時間單位):

  時、分、秒、毫秒等ide

5. BlockingQueue(緩存隊列):

  當有任務到來時,會指派給核心線程去執行,等核心線程都被佔用了,那麼再有新的任務,就會加入到隊列中,等隊列滿了,再有任務,就再啓動非核心線程去執行。經常使用的隊列以下spa

  • SynchronousQueue   使用這個隊列時,當有任務到來的時候,它並不存任務,而是直接將任務丟給線程去執行,若是線程都在被佔用,它就會建立線程去處理這個任務,因此通常使用這個緩存隊列的時候,maximumPoolSize(線程池能容納的最大線程數量)設置到 Integer.MAX_VALUE,否則任務數超過maximumPoolSize限制而建立不了線程。線程

  • LinkedBlockingQueue   使用這個隊列是,當有任務到來的時候,若是當前的核心線程數 < corePoolSize,它會新建核心線程去執行任務,若是當前核心線程數 >= corePoolSize時,它會將還未被執行的任務存儲起來,等待執行,可是這個隊列,沒有存儲上限,因此尼,這也就形成了,maximumPoolSize(總線程數),永遠不會超過corePoolSize。此隊列按 FIFO(先進先出)原則對任務進行操做。3d

  • ArrayBlockingQueue   使用這個隊列尼,能夠設置隊列的長度,那麼當任務到來的時候,核心線程數 < corePoolSize時, 則建立核心線程去執行任務,若是核心線程數 >= corePoolSize時,加入到隊列裏面,等待執行,若是隊列也滿了,則新建非核心線程去執行任務。此隊列按 FIFO(先進先出)原則對元素進行操做。可是線程數不能超過總線程數。code

  • DelayQueue(延時隊列)   任務到來時,首先先加入到隊列中,只有達到了指定的延時時間,纔會執行任務。cdn

5. ThreadFactory(線程工廠):

  用來建立線程池中的線程,用默認的便可。

6. RejectedExecutionHandler(拒絕策略):

  當任務過多時,即:當前線程數已經達到了最大線程數,緩衝隊列也已經滿了,或者線程池關閉了,那麼再來的任務請求,咱們會拒絕,怎麼拒絕尼?有如下幾個方案:

  • AbortPolicy(默認策略)

  「當你有了,原配,想再娶個小三時,原配的態度」,默認策略,簡單粗暴,直接拒絕拋異常(RejectedExecutionException)

  • DiscardPolicy

  「原配的太粗暴,無法子,只能把小三的電話,微信都刪掉了,不再來往了」,DiscardPolicy策略就是,直接丟棄,可是不拋異常。若是線程隊列已滿,則後續提交的任務都會被丟棄。

  • DiscardOldestPolicy

  「可是原配看久了,很膩了,算了,人生短短几十年,何須委屈本身,休妻,騰地方,娶小三」,DiscardOldestPolicy策略就是,直接丟棄掉隊伍最前面的任務,再從新提交後面新來的任務。

  • CallerRunsPolicy

      「唉,原配雖然很差看,可是家裏有背景,不敢隨意拋棄,後面的小三仍是哪來的回哪去吧,找個熟人照顧着」,CallerRunsPolicy策略就是不拋棄任務,由調用者運行這個任務,好比主線程啓動了線程池去運行這個任務,如今線程池滿了,那麼這個任務就由主線程進行調用執行了。

總結一下 讓任務到來的時候,會執行如下的流程

  1. 線程數量未達到 corePoolSize,則新建一個線程(核心線程)執行任務。
  2. 線程數量達到了 corePoolsSize,則將任務移入隊列等待。
  3. 隊列已滿,新建非核心線程(先進先出)執行任務。
  4. 隊列已滿,總線程數又達到了 maximumPoolSize,就會由 RejectedExecutionHandler 拋出異常。

明白了嗎?若是你不想本身寫一個線程池,有更簡單的方式,就是系統已經爲咱們定義好了幾個線程池,下面介紹下他們的使用,看看有沒有符合你要求的,。


4.經常使用的線程池

看下圖


5.代碼

1. FixedThreadPool

建立一個定長的線程池,可控制線程最大的併發數,超出的部分任務,會在隊列中等待,適用於:已知併發壓力的狀況下,對線程數作限制。實際上就是隻使用核心線程。

private void testFiexdThreadPool() {

        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        for (int n = 0; n < 10; n++){

            final int finalN = n;
            threadPool.execute(new Runnable() {

                @Override
                public void run() {

                    System.out.println(Thread.currentThread().getName()+":"+ finalN);
                }
            });
        }
    }
複製代碼

結果

2. SingleThreadExecutor

建立一個單線程化的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行。

private void testSingleThreadExecutor(){

    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    for (int n = 0; n < 10; n++){

        final int finalN = n;
        threadPool.execute(new Runnable() {

            @Override
            public void run() {

                System.out.println(Thread.currentThread().getName()+":"+ finalN);
            }
        });
    }
}
複製代碼

結果

3. CachedThreadPool

建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。

private void testCachedThreadPool(){

    ExecutorService threadPool = Executors.newCachedThreadPool();

    for (int n = 0; n < 10; n++){

        final int finalN = n;
        threadPool.execute(new Runnable() {

            @Override
            public void run() {

                System.out.println(Thread.currentThread().getName()+":"+ finalN);
            }
        });
    }
}
複製代碼

結果

4. ScheduledThreadPool

建立一個可按期或者延時執行任務的定長線程池,支持定時及週期性任務執行。

private void testScheduledThreadPool(){

    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);

    for (int n = 0; n < 10; n++){

        final int finalN = n;
        threadPool.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {

                System.out.println(Thread.currentThread().getName()+":"+ finalN);
            }
        }, 3, 2, TimeUnit.SECONDS);
    }
}
複製代碼

結果

相關文章
相關標籤/搜索