撩一撩Java線程池ThreadPoolExecutor

什么是ThreadPoolExecutor?

JDK1.5開始出現(xiàn),繼承關(guān)系如下:

ThreadPoolExecutor <- AbstractExecutorService <-ExecutorService <- Executor

后面兩個(gè)是接口,官方注釋表明主要是解決兩個(gè)問題:
1.當(dāng)執(zhí)行大量異步任務(wù)時(shí)候線程池能夠提供較好的性能,這是因?yàn)槭褂镁€程池可以使每個(gè)任務(wù)的調(diào)用開銷減少。
2.線程池提供了一種資源限制和管理的手段,比如當(dāng)執(zhí)行一系列任務(wù)時(shí)候?qū)€程的管理,每個(gè)ThreadPoolExecutor也保留了一些基本的統(tǒng)計(jì)數(shù)據(jù),比如當(dāng)前線程池完成的任務(wù)數(shù)目。

為什么系統(tǒng)會(huì)給我們提供線程池呢?
新建線程的缺點(diǎn)如下:
a. 每次new Thread新建對象性能差。
b. 線程缺乏統(tǒng)一管理,可能無限制新建線程,相互之間競爭,及可能占用過多系統(tǒng)資源導(dǎo)致死機(jī)或oom。
c. 缺乏更多功能,如定時(shí)執(zhí)行、定期執(zhí)行、線程中斷。
相比new Thread,四種線程池的好處在于:
a. 重用存在的線程,減少對象創(chuàng)建、消亡的開銷,性能佳。
b. 可有效控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過多資源競爭,避免堵塞。
c. 提供定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能。

如何使用ThreadPoolExecutor

1.使用Executors工具類(讓我想到了Collections)里面已有的靜態(tài)方法創(chuàng)建

        val newFixedThreadPool = Executors.newFixedThreadPool(20)
        val newSingleThreadExecutor = Executors.newSingleThreadExecutor()
        val newCachedThreadPool = Executors.newCachedThreadPool()
        val newScheduledThreadPool = Executors.newScheduledThreadPool(20)

2.ThreadPoolExecutort提供了四個(gè)構(gòu)造方法,我們可以直接創(chuàng)建自定義的線程池,方法1 本質(zhì)上也是通過ThreadPoolExecutort創(chuàng)建的,只是幫我們封裝好了。


構(gòu)造方法

相關(guān)參數(shù)解釋如下:

序號(hào) 參數(shù)名 參數(shù)類型 參數(shù)含義
1 corePoolSize int 核心線程池大小
2 maximumPoolSize int 最大線程池大小
3 keepAliveTime long 線程最大空閑時(shí)間
4 unit TimeUnit 時(shí)間單位
5 workQueue BlockingQueue<Runnable> 線程等待隊(duì)列
6 threadFactory ThreadFactory 線程創(chuàng)建工廠
7 handler RejectedExecutionHandler 拒絕策略

我們先從系統(tǒng)提供的方法來深入了解下每個(gè)參數(shù)的含義

1.newFixedThreadPool(20)

     public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

這個(gè)方法有1 個(gè)參數(shù),int threads 表示線程池中的數(shù)量,這個(gè)參數(shù)會(huì)直接給ThreadPoolExecutor的第一和第二個(gè)參數(shù),也就是corePoolSize和maximumPoolSize,剩下的參數(shù)中空余時(shí)間給的是 0,創(chuàng)建了一個(gè)新建了阻塞隊(duì)列LinkedBlockingQueue,返回值是ExecutorService。

2.newSingleThreadExecutor()

   public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

這個(gè)方法沒有參數(shù),和newFixedThreadPool區(qū)別是corePoolSize和maximumPoolSize兩個(gè)參數(shù)給了都是 1,需要注意這里最終返回的是FinalizableDelegatedExecutorService。有個(gè)問題想一想,一個(gè)線程也要用線程池么?意義在哪里?

3.newCachedThreadPool()

   public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

這個(gè)看起來參數(shù)都挺全的,首先corePoolSize是 0,maximumPoolSize給了 int 最大值,keepAliveTime是 60 ,時(shí)間單位是秒,這里注意等待隊(duì)列和前面兩個(gè)不一樣這里是SynchronousQueue。返回值是ExecutorService。

4.newScheduledThreadPool(20)

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

這個(gè)高級點(diǎn)了,自己是個(gè)對象當(dāng)然是集成ThreadPoolExecutor類,我們看下他的構(gòu)造方法有兩個(gè),這里直接看一個(gè)參數(shù)的構(gòu)造方法。返回值是ScheduledThreadPoolExecutor,這里注意和前面三個(gè)都不一樣。

  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

同樣這里入?yún)⒑颓逦耍瑓?shù)名是corePoolSize核心線程數(shù)量直接給了我們ThreadPoolExecutor的第一個(gè)參數(shù)corePoolSize,第二個(gè)maximumPoolSize最大線程池大小和newCachedThreadPool一樣給的是 int 最大值,第三個(gè)看起來是個(gè)常量我們看下代碼中是 DEFAULT_KEEPALIVE_MILLIS = 10L ,單位是秒,這里的等待隊(duì)列和前面的也不一樣是DelayedWorkQueue優(yōu)先級隊(duì)列。

到此系統(tǒng)提供的四種創(chuàng)建線程池的參數(shù)都一一看了一遍,接下來我們看下為什么系統(tǒng)要提供這四種方法給我們,根據(jù)構(gòu)造方法中的參數(shù),有什么不一樣的使用場景,可以先想一想不著急忘記下。

現(xiàn)在就開始分析下四個(gè)方法創(chuàng)建出的線程池的使用場景。

1. newFixedThreadPool(白話文:固定數(shù)目的線程池)

通過上面的構(gòu)造方法我們知道,newFixedThreadPool核心線程池等于最大線程池,當(dāng)前的線程數(shù)能夠比較穩(wěn)定保證一個(gè)數(shù)。能夠避免頻繁回收線程和創(chuàng)建線程。故適用于處理cpu密集型的任務(wù),確保cpu在長期被工作線程使用的情況下,盡可能少的分配線程,即適用長期的任務(wù)。
缺點(diǎn):
達(dá)到線程池最大容量后,如果有任務(wù)完成讓出占用線程,那么此線程就會(huì)一直處于等待狀態(tài),而不會(huì)消亡,直到下一個(gè)任務(wù)再次占用該線程。這就可能會(huì)使用無界隊(duì)列來存放排隊(duì)任務(wù),當(dāng)大量任務(wù)超過線程池最大容量需要處理時(shí),隊(duì)列無線增大,會(huì)占用大量資源。

2. newSingleThreadExecutor(白話文:唯一線程線程池)

由于是唯一的嘛,最適用串行化任務(wù)。

3. newCachedThreadPool(白話文:可緩存線程的線程池)

newCacehedThreadPool 的最大特點(diǎn)就是,線程數(shù)量不固定。只要有空閑線程空閑時(shí)間超過keepAliveTime,就會(huì)被回收。有新的任務(wù),查看是否有線程處于空閑狀態(tài),如果不是就直接創(chuàng)建新的任務(wù)。故適用用于并發(fā)不固定的短期小任務(wù)。
缺點(diǎn):
線程池沒有最大線程數(shù)量限制,如果大量的任務(wù)同時(shí)提交,可能導(dǎo)致創(chuàng)線程過多會(huì)而導(dǎo)致資源耗盡。

4. newScheduledThreadPool(白話文:定時(shí)及周期執(zhí)行的線程池)

適用于定時(shí)操作一些任務(wù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。