詳談線程池的理解和應用

正因爲我抱着與你相見的但願,我才永遠認爲最崎嶇的路是最好的路。html

1、線程池的好處

線程池是啥子,幹啥使它呀,老子線程使得好好的,非得屢次一舉,哈哈,想必來這裏看這篇文章的都對線程池有點了解。那麼我來整理整理線程池的好處吧。面試

一、線程池的重用

線程的建立和銷燬的開銷是巨大的,而經過線程池的重用大大減小了這些沒必要要的開銷,固然既然少了這麼多消費內存的開銷,其線程執行速度也是日新月異的提高。設計模式

二、控制線程池的併發數

初學新手可能對併發這個詞語比較陌生,特此我也是結合百度百科和必生所學得出最優解釋,萬萬記着併發可跟並行不同。併發

併發:在某個時間段內,多個程序都處在執行和執行完畢之間;但在一個時間點上只有一個程序在運行。頭腦風暴:老鷹媽媽喂小雛鷹食物,小雛鷹不少,而老鷹只有一張嘴,她須要一個個餵過去,到最後每一個小雛鷹均可以吃到,可是在一個時間點裏只能有一個小雛鷹能夠吃到美味的食物。異步

並行:在某個時間段裏,每一個程序按照本身獨立異步的速度執行,程序之間互不干擾。頭腦風暴:這就好似老鷹媽媽決定這樣餵食太費勁因而爲每一個小雛鷹請了個保姆,這樣子在一個時間點裏,每一個小雛鷹均可以同時吃到食物,並且互相不干擾。this

回到線程池,控制線程池的併發數能夠有效的避免大量的線程池爭奪CPU資源而形成堵塞。頭腦風暴:仍是拿老鷹的例子來說,媽媽只有一個,要這麼一個個喂下去,一些餓壞的小雛鷹等不下去了就要破壞規則,搶在靠前餵食的雛鷹面前,而前面的雛鷹也不是吃軟飯的,因而打起來了,場面混亂。老鷹生氣了,這麼不懂事,誰也別吃了,因而形成了最後誰也沒食吃的局面。.net

三、線程池能夠對線程進行管理

線程池能夠提供定時、按期、單線程、併發數控制等功能。好比經過ScheduledThreadPool線程池來執行S秒後,每隔N秒執行一次的任務。線程

2、線程池的詳解

推薦博客: http://blog.csdn.net/seu_calvin/article/details/52415337

想必看完上面那篇博客,你們可謂讚不絕口,不過可能有些小夥伴仍是記不下來,還有些小夥伴以爲好惡心呀,怎麼都是廁所啥的呀!哈哈彆着急,我來給你們一種好記的辦法。設計

先來說講參數最多的那個構造方法,主要是對那幾個煩人的參數進行分析。code

一、ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    ...
}

這裏是7個參數(咱們在開發中用的更多的是5個參數的構造方法),OK,那咱們來看看這裏七個參數的含義:

  • corePoolSize 線程池中核心線程的數量
  • maximumPoolSize 線程池中最大線程數量
  • keepAliveTime 非核心線程的超時時長,當系統中非核心線程閒置時間超過keepAliveTime以後,則會被回收。若是ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置爲true,則該參數也表示核心線程的超時時長
  • unit 第三個參數的單位,有納秒、微秒、毫秒、秒、分、時、天等
  • workQueue 線程池中的任務隊列,該隊列主要用來存儲已經被提交可是還沒有執行的任務。存儲在這裏的任務是由ThreadPoolExecutor的execute方法提交來的。
  • threadFactory 爲線程池提供建立新線程的功能,這個咱們通常使用默認便可
  • handler 拒絕策略,當線程沒法執行新任務時(通常是因爲線程池中的線程數量已經達到最大數或者線程池關閉致使的),默認狀況下,當線程池沒法處理新線程時,會拋出一個RejectedExecutionException。

emmmmm…看到那麼多煩人的概念,是否是有點頭大了,我反正是頭大了。

這7個參數中,日常最多用到的是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue.在這裏我主要抽出corePoolSize、maximumPoolSize和workQueue三個參數進行詳解。

maximumPoolSize(最大線程數) = corePoolSize(核心線程數) + noCorePoolSize(非核心線程數)

(1)當currentSize<corePoolSize時,沒什麼好說的,直接啓動一個核心線程並執行任務。

(2)當currentSize>=corePoolSize、而且workQueue未滿時,添加進來的任務會被安排到workQueue中等待執行。

(3)當workQueue已滿,可是currentSize<maximumPoolSize時,會當即開啓一個非核心線程來執行任務。

(4)當currentSize>=corePoolSize、workQueue已滿、而且currentSize>maximumPoolSize時,調用handler默認拋出RejectExecutionExpection異常。

什麼currentSize,corePoolSize,maximumPoolSize,workQueue比來比去的都比迷糊了,哈哈,那我舉個燒烤店的例子來想必你們理解起來更快。

夏天了,很熱,因此不少燒烤店都會在外面也佈置座位,分爲室內、室外兩個地方能夠吃燒烤。(室內有空調電視,並且室內比室外燒烤更加優惠,並且外面下着瓢潑大雨因此顧客會首先選擇室內)

corePoolSize(燒烤店室內座位),currentPoolSize(目前到燒烤店的顧客數量),maximumPoolSize(燒烤店室內+室外+侯廳室全部座位),workQueue(燒烤店爲顧客專門設置的侯廳室)

第(1)種,燒烤店人數很少的時候,室內位置不少,你們都其樂融融,開心的坐在室內吃着燒烤,看着世界盃。

第(2)種,生意不錯,室內燒烤店坐無空席,你們都不肯意去外面吃,因而在侯廳室裏呆着,侯廳室位置沒坐滿。

第(3)種,生意興隆,室內、侯廳室都坐無空席,可是顧客太餓了,剩下的人沒辦法只好淋着雨吃燒烤,哈哈,好可憐。

第(4)種,生意爆棚,室內、室外、侯廳室都坐無空席,再有顧客過來直接趕走。

對於workQueue仍是有點陌生的小夥伴。

推薦博客: http://blog.csdn.net/u0127025...

二、其餘線程池的記法

在這裏主要是跟你們分享一種特別容易記住其餘四種線程池的方法,在你們寫代碼,面試時能夠及時想到這四種線程池。

(1)FixedThreadPool:

Fixed中文解釋爲固定。結合在一塊兒解釋固定的線程池,說的更全面點就是,有固定數量線程的線程池。其corePoolSize=maximumPoolSize,且keepAliveTime爲0,適合線程穩定的場所。

(2)SingleThreadPool:

Single中文解釋爲單一。結合在一塊兒解釋單一的線程池,說的更全面點就是,有固定數量線程的線程池,且數量爲一,從數學的角度來看SingleThreadPool應該屬於FixedThreadPool的子集。其corePoolSize=maximumPoolSize=1,且keepAliveTime爲0,適合線程同步操做的場所。

(3)CachedThreadPool:

Cached中文解釋爲儲存。結合在一塊兒解釋儲存的線程池,說的更通俗易懂,既然要儲存,其容量確定是很大,因此他的corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE(2^32-1一個很大的數字)

(4)ScheduledThreadPool:

Scheduled中文解釋爲計劃。結合在一塊兒解釋計劃的線程池,顧名思義既然涉及到計劃,必然會涉及到時間。因此ScheduledThreadPool是一個具備定時按期執行任務功能的線程池。

3、線程池的單例

什麼是單例呢?咳咳。

一、單例

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種模式涉及到一個單一的類,該類負責建立本身的對象,同時確保只有單個對象被建立。這個類提供了一種訪問其惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。

注意事項:

  • 單例類只能有一個實例。
  • 單例類必須本身建立本身的惟一實例。
  • 單例類必須給全部其餘對象提供這一實例。
推薦: http://www.runoob.com/design-...

二、線程池的單例

那麼問題來了,我線程池用的好好的,用的時候建立一個,不用就無論他,那爲何要將線程池設計成單例模式呢。那麼就要看看你將線程池應用的場所了。通常狀況下,整個系統中只須要單種線程池,多個線程公用一個線程池,不會是每創一個線程就要建立一個線程池,那樣子你還不如不用線程池呢。

言歸正傳,我們來看看如何將線程池設計成單例模式。廢話少說上代碼

首先在ThreadPool類裏面實現線程池的建立,咱們這裏建立的是FixedThreadPool線程池(記住構造方法要私有,保證不被其餘類實例化)

private ThreadPool(int corepoolsize, int maximumpoolsize, long keepalivetime) {
    this.corepoolsize = corepoolsize;
    this.maximumpoolsize = maximumpoolsize;
    this.keepalivetime = keepalivetime;
}

public void executor(Runnable runnable) {
    if (runnable == null) {
        return;
    }
    if (mexecutor == null) {
        mexecutor = new ThreadPoolExecutor(corepoolsize, //核心線程數
                maximumpoolsize, //最大線程數
                keepalivetime, //閒置線程存活時間
                TimeUnit.MILLISECONDS, // 時間單位
                new LinkedBlockingDeque<Runnable>(), //線程隊列
                Executors.defaultThreadFactory(), //線程工廠
                new ThreadPoolExecutor.AbortPolicy() //隊列已滿,並且當前線程數已經超過最大線程數時的異常處理策略
        );
    }
    mexecutor.execute(runnable);
}

再而後對ThreadPool內部類,在類裏面對他實例化,實現單例

// 獲取單例的線程池對象
public static ThreadPool getThreadPool() {
    if (mThreadPool == null) {
        synchronized (ThreadManager.class) {
            if (mThreadPool == null) {
                int cpuNum = Runtime.getRuntime().availableProcessors();// 獲取處理器數量
                int threadNum = cpuNum * 2 + 1;// 根據cpu數量,計算出合理的線程併發數
                mThreadPool = new ThreadPool(threadNum, threadNum, 0L);
            }
        }
    }
    return mThreadPool;
}
相關文章
相關標籤/搜索