Android中的線程池 ThreadPoolExecutor

線程池的優(yōu)點(diǎn):

  1. 重用線程池中的線程,避免因?yàn)榫€程的創(chuàng)建和銷毀帶來的性能消耗
  2. 能有效的控制線程的最大并發(fā)數(shù),避免大量的線程之間因搶占系統(tǒng)資源而導(dǎo)致的阻塞現(xiàn)象
  3. 能夠?qū)€程進(jìn)行簡單的管理,并提供定時(shí)執(zhí)行以及指定間隔循環(huán)執(zhí)行等功能

ThreadPoolExecutor:

Android中,用ThreadPoolExecutor來實(shí)現(xiàn)線程池的配置。
ThreadPoolExecutor文檔中文版
ThreadPoolExecutor文檔英文版

QQ截圖20160711141312.png

ThreadPoolExecutor的構(gòu)造方法

ThreadPoolExecutor的構(gòu)造方法有四個(gè),其實(shí)現(xiàn)如下:
```
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                          TimeUnit unit, BlockingQueue<Runnable> workQueue, 
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            threadFactory, defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                          TimeUnit unit, BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            Executors.defaultThreadFactory(), handler);
}

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;
}
```
構(gòu)造方法的參數(shù)
  • corePoolSize
    程池中的核心線程數(shù),也就是是線程池中的最小線程數(shù);
    核心線程在allowCoreThreadTimeout被設(shè)置為true時(shí)會(huì)超時(shí)退出,默認(rèn)情況下不會(huì)退出;

  • maximumPoolSize
    最大線程池大小,當(dāng)活動(dòng)線程數(shù)達(dá)到這個(gè)值,后續(xù)任務(wù)會(huì)被阻塞

  • keepAliveTime
    線程池中超過corePoolSize數(shù)目的非核心線程最大存活時(shí)間;閑置時(shí)的超時(shí)時(shí)長,超過這個(gè)值后,閑置線程就會(huì)被回收

  • unit
    keepAliveTime 參數(shù)的時(shí)間單位。這是一個(gè)枚舉,詳情請(qǐng)參考TimeUnit

  • workQueue
    執(zhí)行前用于保持任務(wù)的隊(duì)列,也就是線程池的緩存隊(duì)列。此隊(duì)列僅保持由 execute 方法提交的 Runnable 任務(wù)
    關(guān)于三種提交策略這篇文章不錯(cuò)

  • threadFactory
    線程工廠,為線程池提供創(chuàng)建新線程的功能,它是一個(gè)接口,只有一個(gè)方法:Thread newThread(Runnable r)

  • RejectedExecutionHandler
    線程池對(duì)拒絕任務(wù)的處理策略。一般是隊(duì)列已滿或者無法成功執(zhí)行任務(wù),這時(shí)ThreadPoolExecutor會(huì)調(diào)用handler的rejectedExecution方法來通知調(diào)用者
    ThreadPoolExecutor默認(rèn)有四個(gè)拒絕策略:

      1、ThreadPoolExecutor.AbortPolicy()   直接拋出異常RejectedExecutionException
      2、ThreadPoolExecutor.CallerRunsPolicy()    直接調(diào)用run方法并且阻塞執(zhí)行
      3、ThreadPoolExecutor.DiscardPolicy()   直接丟棄后來的任務(wù)
      4、ThreadPoolExecutor.DiscardOldestPolicy()  丟棄在隊(duì)列中隊(duì)首的任務(wù)
    

也可以自己繼承RejectedExecutionHandler來寫拒絕策略.

ThreadPoolExecutor的執(zhí)行過程:

一個(gè)任務(wù)通過 execute(Runnable)方法被添加到線程池,任務(wù)就是一個(gè) Runnable類型的對(duì)象,任務(wù)的執(zhí)行方法就是Runnable類型對(duì)象的run()方法。

  1. 當(dāng)線程池小于corePoolSize時(shí),新提交任務(wù)將創(chuàng)建一個(gè)新線程執(zhí)行任務(wù),即使此時(shí)線程池中存在空閑線程
  2. 當(dāng)線程池達(dá)到corePoolSize時(shí),新提交任務(wù)將被放入workQueue中,等待線程池中任務(wù)調(diào)度執(zhí)行
  3. 當(dāng)提交任務(wù)數(shù)超過【maximumPoolSize+阻塞隊(duì)列大小】時(shí),新提交任務(wù)由RejectedExecutionHandler處理 (關(guān)于這里,網(wǎng)上90%以上的人說當(dāng)任務(wù)數(shù)>=maximumPoolSize時(shí)就會(huì)被拒絕,我不知道依據(jù)在哪里,也不知道代碼驗(yàn)證過沒,經(jīng)過我的驗(yàn)證這種說法是不成立的,具體的看下邊日志分析)
  4. 當(dāng)線程池中超過corePoolSize線程,空閑時(shí)間達(dá)到keepAliveTime時(shí),關(guān)閉空閑線程
  5. 當(dāng)設(shè)置allowCoreThreadTimeOut(true)時(shí),線程池中corePoolSize線程空閑時(shí)間達(dá)到keepAliveTime也將關(guān)閉
定制自己的線程池:
public class ThreadTestActivity extends AppCompatActivity {
    private final int CORE_POOL_SIZE = 1;//核心線程數(shù)
    private final int MAX_POOL_SIZE = 3;//最大線程數(shù)
    private final int BLOCK_SIZE = 2;//阻塞隊(duì)列大小
    private final long KEEP_ALIVE_TIME = 2;//空閑線程超時(shí)時(shí)間
    private ThreadPoolExecutor executorPool;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread_test);
        //創(chuàng)建線程池
        // 創(chuàng)建一個(gè)核心線程數(shù)為3、最大線程數(shù)為8,緩存隊(duì)列大小為5的線程池
        executorPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(BLOCK_SIZE),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        executorPool.allowCoreThreadTimeOut(true);
    }

    public void begin(View view) {
        for (int num = 0; num < 6; num++) {//每個(gè)500ms添加一個(gè)任務(wù)到隊(duì)列中
            try {
                Li("execute");// 監(jiān)聽相關(guān)數(shù)據(jù)
                executorPool.execute(new WorkerThread("thread-" + num));
            } catch (Exception e) {
                Log.e("threadtest", "AbortPolicy...");
            }
        }

        // 20s后,所有任務(wù)已經(jīng)執(zhí)行完畢,我們?cè)诒O(jiān)聽一下相關(guān)數(shù)據(jù)
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(20 * 1000);
                } catch (Exception e) {

                }
                Li("monitor after");
            }
        }).start();
    }

    private void Li(String mess) {
        Log.i("threadtest", "monitor " + mess
                        + " CorePoolSize:" + executorPool.getCorePoolSize()
                        + " PoolSize:" + executorPool.getPoolSize()
                        + " MaximumPoolSize:" + executorPool.getMaximumPoolSize()
                        + " ActiveCount:" + executorPool.getActiveCount()
                        + " TaskCount:" + executorPool.getTaskCount()

        );
    }

}

// 模擬耗時(shí)任務(wù)
public class WorkerThread implements Runnable {
    private String threadName;
    public WorkerThread(String threadName) {
        this.threadName = threadName;
    }
    @Override
    public synchronized void run() {

        int i = 0;
        boolean flag = true;
        try {
            while (flag) {
                Thread.sleep(1000);
                i++;
                Log.e("threadtest", "WorkerThread " + threadName + "  " + i);
                if (i >2) flag = false;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public String getThreadName() {
        return threadName;
    }
}

日志信息:


QQ截圖20160712161004.png

下面就來對(duì)日志信息進(jìn)行分析:

  1. 上邊粉色部分(1~6行),可以看到poolsize逐漸累加,一直加到最大線程數(shù)后不再增加,這呼應(yīng)了上述“執(zhí)行過程1”
  2. 接下來綠色部分()7行,驗(yàn)證了上邊我們說的“執(zhí)行過程3”,緩存隊(duì)列數(shù)為2,最大線程數(shù)為3,共有6條任務(wù),所以會(huì)有【 6-(2+3)】條任務(wù)被拒絕,這里拒絕策略我們用的是ThreadPoolExecutor.AbortPolicy()也就是直接拋出異常,也就是我們?nèi)罩镜牡?行
  3. 然后8~22行是任務(wù)的執(zhí)行過程,
  4. 其中藍(lán)色部分(8~16)行,我們可以看到有3條任務(wù)在同時(shí)執(zhí)行,也就是最大線程數(shù)
  5. 接下來的綠色(17~22行),在三條任務(wù)執(zhí)行完成后,剩余的排隊(duì)任務(wù)才開始執(zhí)行
  6. 最后,23行,20s后,線程都處于空閑狀態(tài),所以非核心線程會(huì)被回收,但是因?yàn)榇a中我們?cè)O(shè)置了executorPool.allowCoreThreadTimeOut(true),所以這時(shí)處于空閑狀態(tài)的核心線程也會(huì)被回收,這時(shí)池中的線程數(shù)為0
關(guān)于線程池的一些建議
  • 最大線程數(shù)一般設(shè)為2N+1最好,N是CPU核數(shù)

官方定義的四種線程池

其實(shí),本應(yīng)該先說官方定義的這四種線程池,然后再說自定義線程池,但是考慮到里邊的一些配置參數(shù),所以本帖先利用自定義線程池把各個(gè)配置參數(shù)理一下,然后再講官方定義的四種線程池,這樣也便于理解官方定義的這四種線程池
這四種線程池都是通過Executors的工廠方法來實(shí)現(xiàn)

1、FixedThreadPool

他是一種數(shù)量固定的線程池,且任務(wù)隊(duì)列也沒有大小限制;
它只有核心線程,且這里的核心線程也沒有超時(shí)限制,所以即使線程處于空閑狀態(tài)也不會(huì)被回收,除非線程池關(guān)閉;
當(dāng)所有的任務(wù)處于活動(dòng)狀態(tài),新任務(wù)都處于等待狀態(tài),知道所有線程空閑出來;
因?yàn)樗粫?huì)被回收,所以它能更快的響應(yīng);
源碼:

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

實(shí)現(xiàn):

    ExecutorService service = Executors.newFixedThreadPool(3);
    service.execute(new WorkerThread("thread-" + num));
2、CachedThreadPool

無界線程池,可以進(jìn)行自動(dòng)線程回收
他是一種線程數(shù)量不固定的線程池;
它只有非核心線程,且最大線程數(shù)為Integer.MAX_VALUE,也就是說線程數(shù)可以任意大;
當(dāng)池中的線程都處于活動(dòng)狀態(tài)時(shí),會(huì)創(chuàng)建新的線程來處理任務(wù),否則會(huì)利用空閑線程來處理任務(wù);所以,任何添加進(jìn)來的任務(wù)都會(huì)被立即執(zhí)行;
池中的空閑線程都有超時(shí)限制,為60s,超過這個(gè)限制就會(huì)被回收,當(dāng)池中的所有線程都處于閑置狀態(tài)時(shí),都會(huì)因超時(shí)而被回收,這個(gè)時(shí)候,她幾乎不占用任何系統(tǒng)資源;
適合做大量的耗時(shí)較少的任務(wù);
源碼:

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

實(shí)現(xiàn):

    ExecutorService service = Executors.newCachedThreadPool();
    service.execute(new WorkerThread("thread-"));
3、SingleThreadExecutor

只有一個(gè)核心線程,所有任務(wù)都在同一線程中按序執(zhí)行,這樣也就不需要處理線程同步的問題;
源碼:

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

實(shí)現(xiàn):

    ExecutorService service = Executors.newSingleThreadExecutor();
    service.execute(new WorkerThread("thread-"));
4、ScheduledThreadPool

它的核心線程數(shù)量是固定的,而非核心線程是沒有限制的,且非核心線程空閑時(shí)會(huì)被回收;
適合執(zhí)行定時(shí)任務(wù)和具有固定周期的任務(wù)
源碼:

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

實(shí)現(xiàn):

    ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
    或
    ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();

    threadPool.schedule(runnable, 20, TimeUnit.SECONDS);// 20秒后執(zhí)行任務(wù)
    或
    threadPool.scheduleAtFixedRate(runnable,10,20,TimeUnit.SECONDS);//延遲10s,每20s執(zhí)行一次任務(wù)

由于本人技術(shù)有限,避免不了出現(xiàn)一些錯(cuò)誤或者理解有偏差描述不清楚的地方,請(qǐng)大家諒解并提醒我:)

上一篇:AsyncTask
再來一篇:java中的thread

更多內(nèi)容請(qǐng)關(guān)注我的Android專題
本文出自:http://www.lxweimin.com/users/c1b4a5542220/latest_articles
轉(zhuǎn)載請(qǐng)注明出處!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評(píng)論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評(píng)論 3 421
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,935評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評(píng)論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,534評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,892評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,067評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,322評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評(píng)論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評(píng)論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,800評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,084評(píng)論 2 375

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