Java常用四大線程池用法以及ThreadPoolExecutor詳解

為什么用線程池?

  • 1.創(chuàng)建/銷毀線程伴隨著系統(tǒng)開(kāi)銷搬男,過(guò)于頻繁的創(chuàng)建/銷毀線程,會(huì)很大程度上影響處-理效率
  • 2.線程并發(fā)數(shù)量過(guò)多举塔,搶占系統(tǒng)資源從而導(dǎo)致阻塞
  • 3.對(duì)線程進(jìn)行一些簡(jiǎn)單的管理

在Java中输玷,線程池的概念是Executor這個(gè)接口,具體實(shí)現(xiàn)為ThreadPoolExecutor類立砸,學(xué)習(xí)Java中的線程池,就可以直接學(xué)習(xí)他了對(duì)線程池的配置,就是對(duì)ThreadPoolExecutor構(gòu)造函數(shù)的參數(shù)的配置

一呀页、ThreadPoolExecutor提供了四個(gè)構(gòu)造函數(shù)

//五個(gè)參數(shù)的構(gòu)造函數(shù)
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

//六個(gè)參數(shù)的構(gòu)造函數(shù)-1
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

//六個(gè)參數(shù)的構(gòu)造函數(shù)-2
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)

//七個(gè)參數(shù)的構(gòu)造函數(shù)
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

下面來(lái)解釋下各個(gè)參數(shù):

  • int corePoolSize:該線程池中核心線程數(shù)最大值

核心線程:線程池新建線程的時(shí)候,如果當(dāng)前線程總數(shù)小于corePoolSize拥坛,則新建的是核心線程蓬蝶,如果超過(guò)corePoolSize尘分,則新建的是非核心線程核心線程默認(rèn)情況下會(huì)一直存活在線程池中,即使這個(gè)核心線程啥也不干(閑置狀態(tài))丸氛。
如果指定ThreadPoolExecutor的allowCoreThreadTimeOut這個(gè)屬性為true培愁,那么核心線程如果不干活(閑置狀態(tài))的話,超過(guò)一定時(shí)間(時(shí)長(zhǎng)下面參數(shù)決定)缓窜,就會(huì)被銷毀掉定续。

  • int maximumPoolSize: 該線程池中線程總數(shù)最大值

線程總數(shù) = 核心線程數(shù) + 非核心線程數(shù)。

  • long keepAliveTime:該線程池中非核心線程閑置超時(shí)時(shí)長(zhǎng)

一個(gè)非核心線程禾锤,如果不干活(閑置狀態(tài))的時(shí)長(zhǎng)超過(guò)這個(gè)參數(shù)所設(shè)定的時(shí)長(zhǎng)私股,就會(huì)被銷毀掉,如果設(shè)置allowCoreThreadTimeOut = true恩掷,則會(huì)作用于核心線程倡鲸。

  • TimeUnit unit:keepAliveTime的單位

TimeUnit是一個(gè)枚舉類型,其包括:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小時(shí)
DAYS : 天

  • BlockingQueue workQueue:該線程池中的任務(wù)隊(duì)列:維護(hù)著等待執(zhí)行的Runnable對(duì)象

當(dāng)所有的核心線程都在干活時(shí)黄娘,新添加的任務(wù)會(huì)被添加到這個(gè)隊(duì)列中等待處理峭状,如果隊(duì)列滿了,則新建非核心線程執(zhí)行任務(wù)逼争。
常用的workQueue類型:

  • SynchronousQueue:這個(gè)隊(duì)列接收到任務(wù)的時(shí)候优床,會(huì)直接提交給線程處理,而不保留它氮凝,如果所有線程都在工作怎么辦羔巢?那就新建一個(gè)線程來(lái)處理這個(gè)任務(wù)!所以為了保證不出現(xiàn)<線程數(shù)達(dá)到了maximumPoolSize而不能新建線程>的錯(cuò)誤罩阵,使用這個(gè)類型隊(duì)列的時(shí)候竿秆,maximumPoolSize一般指定成Integer.MAX_VALUE,即無(wú)限大

  • LinkedBlockingQueue:這個(gè)隊(duì)列接收到任務(wù)的時(shí)候稿壁,如果當(dāng)前線程數(shù)小于核心線程數(shù)幽钢,則新建線程(核心線程)處理任務(wù);如果當(dāng)前線程數(shù)等于核心線程數(shù)傅是,則進(jìn)入隊(duì)列等待匪燕。由于這個(gè)隊(duì)列沒(méi)有最大值限制,即所有超過(guò)核心線程數(shù)的任務(wù)都將被添加到隊(duì)列中喧笔,這也就導(dǎo)致了maximumPoolSize的設(shè)定失效帽驯,因?yàn)榭偩€程數(shù)永遠(yuǎn)不會(huì)超過(guò)corePoolSize

  • ArrayBlockingQueue:可以限定隊(duì)列的長(zhǎng)度,接收到任務(wù)的時(shí)候书闸,如果沒(méi)有達(dá)到corePoolSize的值尼变,則新建線程(核心線程)執(zhí)行任務(wù),如果達(dá)到了浆劲,則入隊(duì)等候嫌术,如果隊(duì)列已滿哀澈,則新建線程(非核心線程)執(zhí)行任務(wù),又如果總線程數(shù)到了maximumPoolSize度气,并且隊(duì)列也滿了割按,則發(fā)生錯(cuò)誤

  • DelayQueue:隊(duì)列內(nèi)元素必須實(shí)現(xiàn)Delayed接口,這就意味著你傳進(jìn)去的任務(wù)必須先實(shí)現(xiàn)Delayed接口磷籍。這個(gè)隊(duì)列接收到任務(wù)時(shí)适荣,首先先入隊(duì),只有達(dá)到了指定的延時(shí)時(shí)間择示,才會(huì)執(zhí)行任務(wù)

  • ThreadFactory threadFactory:創(chuàng)建線程的方式束凑,這是一個(gè)接口,你new他的時(shí)候需要實(shí)現(xiàn)他的Thread newThread(Runnable r)方法栅盲,一般用不上汪诉。

  • RejectedExecutionHandler handler:這玩意兒就是拋出異常專用的,比如上面提到的兩個(gè)錯(cuò)誤發(fā)生了谈秫,就會(huì)由這個(gè)handler拋出異常扒寄,根本用不上。

二拟烫、向ThreadPoolExecutor添加任務(wù)

我們?cè)趺粗纍ew一個(gè)ThreadPoolExecutor该编,大概知道各個(gè)參數(shù)是干嘛的,可是我new完了硕淑,怎么向線程池提交一個(gè)要執(zhí)行的任務(wù)翱慰ⅰ?

ThreadPoolExecutor.execute(Runnable command)

通過(guò)ThreadPoolExecutor.execute(Runnable command)方法即可向線程池內(nèi)添加一個(gè)任務(wù)置媳。

三于樟、ThreadPoolExecutor的策略

這里給總結(jié)一下,當(dāng)一個(gè)任務(wù)被添加進(jìn)線程池時(shí)拇囊,執(zhí)行策略:

  • 1.線程數(shù)量未達(dá)到corePoolSize迂曲,則新建一個(gè)線程(核心線程)執(zhí)行任務(wù)
  • 2.線程數(shù)量達(dá)到了corePools,則將任務(wù)移入隊(duì)列等待
  • 3.隊(duì)列已滿寥袭,新建線程(非核心線程)執(zhí)行任務(wù)
  • 4.隊(duì)列已滿路捧,總線程數(shù)又達(dá)到了maximumPoolSize,就會(huì)由(RejectedExecutionHandler)拋出異常

+++++++++++++++++++++++++++我是分割線++++++++++++++++++++++++++++


常見(jiàn)四種線程池:

如果你不想自己寫一個(gè)線程池传黄,Java通過(guò)Executors提供了四種線程池杰扫,這四種線程池都是直接或間接配置ThreadPoolExecutor的參數(shù)實(shí)現(xiàn)的。

1.可緩存線程池CachedThreadPool()

源碼:

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

根據(jù)源碼可以看出:

  1. 這種線程池內(nèi)部沒(méi)有核心線程膘掰,線程的數(shù)量是有沒(méi)限制的涉波。
  2. 在創(chuàng)建任務(wù)時(shí),若有空閑的線程時(shí)則復(fù)用空閑的線程炭序,若沒(méi)有則新建線程啤覆。
  3. 沒(méi)有工作的線程(閑置狀態(tài))在超過(guò)了60S還不做事,就會(huì)銷毀惭聂。

創(chuàng)建方法:

ExecutorService mCachedThreadPool = Executors.newCachedThreadPool();

用法:

//開(kāi)始下載
private void startDownload(final ProgressBar progressBar, final int i) {
        mCachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                int p = 0;
                progressBar.setMax(10);//每個(gè)下載任務(wù)10秒
                while (p < 10) {
                    p++;
                    progressBar.setProgress(p);
                    Bundle bundle = new Bundle();
                    Message message = new Message();
                    bundle.putInt("p", p);
                    //把當(dāng)前線程的名字用handler讓textview顯示出來(lái)
                    bundle.putString("ThreadName", Thread.currentThread().getName());
                    message.what = i;
                    message.setData(bundle);
                    mHandler.sendMessage(message);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }
2.FixedThreadPool 定長(zhǎng)線程池

源碼:

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

根據(jù)源碼可以看出:

  1. 該線程池的最大線程數(shù)等于核心線程數(shù)窗声,所以在默認(rèn)情況下,該線程池的線程不會(huì)因?yàn)殚e置狀態(tài)超時(shí)而被銷毀辜纲。
  2. 如果當(dāng)前線程數(shù)小于核心線程數(shù)笨觅,并且也有閑置線程的時(shí)候提交了任務(wù),這時(shí)也不會(huì)去復(fù)用之前的閑置線程耕腾,會(huì)創(chuàng)建新的線程去執(zhí)行任務(wù)见剩。如果當(dāng)前執(zhí)行任務(wù)數(shù)大于了核心線程數(shù),大于的部分就會(huì)進(jìn)入隊(duì)列等待扫俺。等著有閑置的線程來(lái)執(zhí)行這個(gè)任務(wù)苍苞。

創(chuàng)建方法:

//nThreads => 最大線程數(shù)即maximumPoolSize
ExecutorService mFixedThreadPool= Executors.newFixedThreadPool(int nThreads);

//threadFactory => 創(chuàng)建線程的方法,用得少
ExecutorService mFixedThreadPool= Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);

用法:

private void startDownload(final ProgressBar progressBar, final int i) {
        mFixedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
               //....邏輯代碼自己控制
            }
        });
    }
3.SingleThreadPool

源碼:

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

根據(jù)源碼可以看出:

  1. 有且僅有一個(gè)工作線程執(zhí)行任務(wù)
  2. 所有任務(wù)按照指定順序執(zhí)行狼纬,即遵循隊(duì)列的入隊(duì)出隊(duì)規(guī)則

創(chuàng)建方法:
ExecutorService mSingleThreadPool = Executors.newSingleThreadPool();

用法同上羹呵。

4.ScheduledThreadPool

源碼:

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

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

根據(jù)源碼可以看出:
DEFAULT_KEEPALIVE_MILLIS就是默認(rèn)10L,這里就是10秒疗琉。這個(gè)線程池有點(diǎn)像是吧CachedThreadPool和FixedThreadPool 結(jié)合了一下冈欢。

  1. 不僅設(shè)置了核心線程數(shù),最大線程數(shù)也是Integer.MAX_VALUE盈简。
  2. 這個(gè)線程池是上述4個(gè)中為唯一個(gè)有延遲執(zhí)行和周期執(zhí)行任務(wù)的線程池凑耻。

創(chuàng)建:

//nThreads => 最大線程數(shù)即maximumPoolSize
ExecutorService mScheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);

一般的執(zhí)行任務(wù)方法和上面的都大同小異,我們主要看看延時(shí)執(zhí)行任務(wù)和周期執(zhí)行任務(wù)的方法柠贤。

//表示在3秒之后開(kāi)始執(zhí)行我們的任務(wù)香浩。
mScheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
            //....
            }
        }, 3, TimeUnit.SECONDS);
//延遲3秒后執(zhí)行任務(wù),從開(kāi)始執(zhí)行任務(wù)這個(gè)時(shí)候開(kāi)始計(jì)時(shí)种吸,每7秒執(zhí)行一次不管執(zhí)行任務(wù)需要多長(zhǎng)的時(shí)間弃衍。 
mScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
             //....
            }
        },3, 7, TimeUnit.SECONDS);
/**延遲3秒后執(zhí)行任務(wù),從任務(wù)完成時(shí)這個(gè)時(shí)候開(kāi)始計(jì)時(shí)坚俗,7秒后再執(zhí)行镜盯,
*再等完成后計(jì)時(shí)7秒再執(zhí)行也就是說(shuō)這里的循環(huán)執(zhí)行任務(wù)的時(shí)間點(diǎn)是
*從上一個(gè)任務(wù)完成的時(shí)候。
*/
mScheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
             //....
            }
        },3, 7, TimeUnit.SECONDS);

以上就是常用的四個(gè)線程池以及他們的實(shí)現(xiàn)原理猖败。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末速缆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恩闻,更是在濱河造成了極大的恐慌艺糜,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異破停,居然都是意外死亡翅楼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門真慢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)毅臊,“玉大人,你說(shuō)我怎么就攤上這事黑界」苕遥” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵朗鸠,是天一觀的道長(zhǎng)蚯撩。 經(jīng)常有香客問(wèn)我,道長(zhǎng)烛占,這世上最難降的妖魔是什么胎挎? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮扰楼,結(jié)果婚禮上呀癣,老公的妹妹穿的比我還像新娘。我一直安慰自己弦赖,他們只是感情好项栏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蹬竖,像睡著了一般沼沈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上币厕,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天列另,我揣著相機(jī)與錄音,去河邊找鬼旦装。 笑死页衙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阴绢。 我是一名探鬼主播店乐,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呻袭!你這毒婦竟也來(lái)了眨八?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤左电,失蹤者是張志新(化名)和其女友劉穎廉侧,沒(méi)想到半個(gè)月后页响,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡段誊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年闰蚕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枕扫。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陪腌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出烟瞧,到底是詐尸還是另有隱情,我是刑警寧澤染簇,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布参滴,位于F島的核電站,受9級(jí)特大地震影響锻弓,放射性物質(zhì)發(fā)生泄漏砾赔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一青灼、第九天 我趴在偏房一處隱蔽的房頂上張望暴心。 院中可真熱鬧,春花似錦杂拨、人聲如沸专普。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)檀夹。三九已至,卻和暖如春策橘,著一層夾襖步出監(jiān)牢的瞬間炸渡,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工丽已, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚌堵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓沛婴,卻偏偏與公主長(zhǎng)得像吼畏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瘸味,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容