Android/java 多線程(五)-ThreadPoolExecutor線程池的使用

多線程系列歷史文章:
Android/java 多線程(一)-Thread的使用以及源碼分析
Android/java 多線程(二)-Thread的好兄弟Handler
Android/java 多線程(三)-HandlerThread的使用場景及源碼解析
Android/java多線程(四)-IntentService

前面幾篇文章主要都是介紹的單個線程的運用以及原理猜煮,這篇文章開始講解多個線程的運用

簡介

線程池是一個能對多個線程進行統(tǒng)一管理的一套機制育叁,它具有諸多的優(yōu)點:

  • 能對線程進行復(fù)用晨横,當(dāng)有空閑的線程莺奸,線程池會復(fù)用這些線程而不會去重新創(chuàng)建格仲,節(jié)省了資源
  • 能靈活創(chuàng)建各種線程使用場景,內(nèi)部封裝了常用線程池創(chuàng)建邏輯,并支持自定義線程池邏輯創(chuàng)建
  • 能準(zhǔn)確的對每一個線程進行管理胎食,并能讀取每個線程的一寫基礎(chǔ)信息,方便進行一些邏輯處理

缺點:
線程中的數(shù)據(jù)傳遞沒有Handler機制方便

使用場景:
具有諸多耗時任務(wù)的情況允懂,當(dāng)系統(tǒng)為每一個任務(wù)創(chuàng)建一個線程厕怜,會占用系統(tǒng)的大量資源,容易引起界面的卡頓,線程池的復(fù)用機制就很好的解決了這個問題

常用的線程池的使用

自定義線程池
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

傳入不同的參數(shù)粥航,實現(xiàn)的效果也不同,下面說一下每個參數(shù)的作用:

  • corePoolSize 核心線程數(shù),琅捏,即使空閑時仍保留在池中的線程數(shù),除非設(shè)置了allowCoreThreadTimeOut
  • maximumPoolSize 線程池中允許的最大線程數(shù)
  • keepAliveTime 顧名思義,線程終止前的存活時間递雀,即當(dāng)線程數(shù)大于核心線程數(shù)(corePoolSize )時,多余的空閑線程在終止之前等待新任務(wù)的最大時間
  • TimeUnit keepAliveTime的單位
  • workQueue 在執(zhí)行任務(wù)之前用于保存任務(wù)的隊列柄延。 該隊列將僅保存execute方法提交的Runnable任務(wù)。
  • ThreadFactory 執(zhí)行程序創(chuàng)建新線程時使用的工廠
  • RejectedExecutionHandler 執(zhí)行被阻止時使用的處理程序

這里說一下各個參數(shù)的配置套路,當(dāng)使用execute(Runnable)方法添加一個任務(wù)到隊列中映之,如果corePoolSizemaximumPoolSize小拦焚,隊列滿了后就會去創(chuàng)建新的線程,當(dāng)corePoolSizemaximumPoolSize相等時,就會創(chuàng)建一個固定大小的核心線程池杠输。如果將maximumPoolSize設(shè)置為無限大(例如nteger.MAX_VALUE)赎败,則是創(chuàng)建一個能容納任意數(shù)量任務(wù)的線程池。
ThreadFactory的作用是用來創(chuàng)建新的線程的蠢甲,它是一個接口,實現(xiàn)它即可創(chuàng)建一個新的線程:

public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}

通過它僵刮,你可以指定線程的名稱,優(yōu)先級鹦牛,守護進程等搞糕,如果不設(shè)置,則會默認使用Executors.defaultThreadFactory()曼追,它會創(chuàng)建一個與所有線程與所有相同的ThreadGroup并且具有相同的優(yōu)先級和非守護進程狀態(tài)NORM_PRIORITY窍仰。
workQueue是一個排隊策略,一般有三種:

  • 直接切換:對應(yīng)SynchronousQueue實現(xiàn)類,將任務(wù)直接交給線程處理礼殊,不需要另外的控制驹吮,通常需要配置一個無限制的maximumPoolSizes,以避免拒絕掉新提交的任務(wù)
  • 無界隊列:對應(yīng)LinkedBlockingDeque實現(xiàn)類晶伦,它沒有預(yù)定的容量碟狞,當(dāng)有新的任務(wù),會在隊列中等待婚陪,直到加入到corePoolSize的線程中族沃,如果corePoolSize線程一直很忙,也不會去創(chuàng)建新的線程泌参,此時脆淹,最大值最小值對它沒有任何的影響,每個任務(wù)與其他的任務(wù)都是獨立的沽一,不會互相影響
  • 有邊界的隊列:例如ArrayBlockingQueue實現(xiàn)類,它有助于在使用有限的maxPoolSize時防止資源的耗盡盖溺,但它更難調(diào)整與控制。

一般情況下我們都可以通過java提供的工廠模式來構(gòu)造我們的線程池策略锯玛,主要提供了以下幾種方式:

newCachedThreadPool

特點:

  • 線程重用,如果沒有重用的線程,將會創(chuàng)建一個新的線程添加到池中
  • 適合執(zhí)行短期異步任務(wù)程序攘残,默認為60秒的線程等待時間拙友,超過就會終止與移除
  • 閑置的線程不會消耗資源

源碼配置:

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

使用場景:
它創(chuàng)建了一個無限擴大的線程池,但沒有核心線程池歼郭,因此資源占用少遗契,適合處理一些短期的異步任務(wù)

newFixedThreadPool

特點:

  • 創(chuàng)建一個固定數(shù)量的線程池,不能隨時的新建線程病曾,如果隊列已滿牍蜂,提交了新任務(wù),必須等待一個可用的線程
  • 線程會一直存在泰涂,直到調(diào)用shutdown方法

源碼配置:

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

使用場景:
它創(chuàng)建了一個核心線程數(shù)與最大線程數(shù)相同的線程池鲫竞,適用于已知任務(wù)數(shù)量,對線程數(shù)量進行限制的場景

newWorkStealingPool

特點:

  • 創(chuàng)建使用所有給定并行級別的線程池逼蒙,并且可以使用多個隊列來減少占用
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

使用場景:
它能創(chuàng)建一個擁有多個任務(wù)隊列的線程池从绘,可以減少鏈接數(shù)。并且它是默認創(chuàng)建當(dāng)前可用cpu數(shù)量的線程來并行執(zhí)行是牢,因此適用于非常耗時的操作僵井,并且可以并行執(zhí)行

newSingleThreadExecutor

特點:

  • 有且只有一個任務(wù)處于活動狀態(tài)
  • 先提交的先執(zhí)行,有任務(wù)順序
  • 當(dāng)任務(wù)出現(xiàn)異常驳棱,會另創(chuàng)建一個新的線程替換
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

使用場景:
它是一個單例線程批什,并且有執(zhí)行順序,所以適用于有執(zhí)行順序的任務(wù)社搅,并且有且只有一個任務(wù)是執(zhí)行中的

ScheduledThreadPoolExecutor

特點:

  • 能設(shè)置延遲時間驻债,能定期執(zhí)行
  • 空閑的線程會保留
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

使用場景:
它具有定時特性,因此可以用來執(zhí)行周期行的任務(wù)

五大線程池介紹完畢罚渐,此外還有其他的常用方法:

  • execute(Runnable command) 提交任務(wù)却汉,在將來的某個時間點執(zhí)行任務(wù)
  • afterExecute(Runnale r,Throwable t) 在指定的Runnale執(zhí)行后調(diào)用此方法
  • beforeExecute(Thread t,Runnable r)在給定的線程中執(zhí)行給定的Runnable之前調(diào)用方法
  • funalize 當(dāng)執(zhí)行器不再被引用并且沒有線程時,調(diào)用shundown
  • shundown 啟動有序關(guān)閉荷并,先前提交的任務(wù)會執(zhí)行合砂,執(zhí)行完畢后關(guān)閉
  • shundownNow 主動停止執(zhí)行中的任務(wù),并返回正在執(zhí)行等待的任務(wù)列表
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末源织,一起剝皮案震驚了整個濱河市翩伪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谈息,老刑警劉巖缘屹,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異侠仇,居然都是意外死亡轻姿,警方通過查閱死者的電腦和手機犁珠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來互亮,“玉大人犁享,你說我怎么就攤上這事”荩” “怎么了炊昆?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長威根。 經(jīng)常有香客問我凤巨,道長,這世上最難降的妖魔是什么洛搀? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任敢茁,我火速辦了婚禮,結(jié)果婚禮上姥卢,老公的妹妹穿的比我還像新娘卷要。我一直安慰自己,他們只是感情好独榴,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布僧叉。 她就那樣靜靜地躺著,像睡著了一般棺榔。 火紅的嫁衣襯著肌膚如雪瓶堕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天症歇,我揣著相機與錄音郎笆,去河邊找鬼。 笑死忘晤,一個胖子當(dāng)著我的面吹牛宛蚓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播设塔,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼凄吏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了闰蛔?” 一聲冷哼從身側(cè)響起痕钢,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎序六,沒想到半個月后任连,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡例诀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年随抠,在試婚紗的時候發(fā)現(xiàn)自己被綠了裁着。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拱她,死狀恐怖跨算,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情椭懊,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布步势,位于F島的核電站氧猬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏坏瘩。R本人自食惡果不足惜盅抚,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望倔矾。 院中可真熱鬧妄均,春花似錦、人聲如沸哪自。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽壤巷。三九已至邑彪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胧华,已是汗流浹背寄症。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留矩动,地道東北人有巧。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像悲没,于是被迫代替她去往敵國和親篮迎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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