讓洪水猛獸變成溫順小羊——淺談Android線程池機(jī)制

寫作原因:Java或者Android開發(fā)者都知道,Java/Android中對于并發(fā)的控制是一個難點(diǎn)。如果控制不好線程問題,程序的穩(wěn)定性難以得到保障。下面博主分享一下Android中線程池機(jī)制的基本內(nèi)容。


原文鏈接:博主小屋

定義

這是Wikipedia對線程池的定義,看看即可:在計(jì)算機(jī)編程中,線程池包含一定數(shù)量的線程,這些線程用于并發(fā)地處理任務(wù)。通常情況下;反之,線程的數(shù)量根據(jù)可用的計(jì)算資源進(jìn)行調(diào)整從而達(dá)到合理地并行處理任務(wù)即使任務(wù)的數(shù)量取決于問題本身并且在處理之前并不知道它們的確切情況??床欢繘]關(guān)系,下面通過具體案例來學(xué)習(xí)使用。

優(yōu)勢

使用線程池有以下一些優(yōu)勢:
(1)最大程度合理地使用線程,從而減少線程創(chuàng)建與銷毀帶來的性能開銷。
(2)有效控制線程池的最大并發(fā)數(shù),避免大量線程之間因?yàn)榛ハ鄵屨枷到y(tǒng)資源而導(dǎo)致阻塞現(xiàn)象。
(3)能對線程進(jìn)行簡單的管理,并且提供定時(shí)執(zhí)行以及指定間隔循環(huán)執(zhí)行等功能。

ThreadPoolExecutor

下面先看看ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory)

上面這段代碼正是構(gòu)成線程池的核心。Java幾乎所有線程池都是直接或者間接使用ThreadPoolExecutor來實(shí)現(xiàn)的,這里先講解一下這個線程池生成器的構(gòu)成和運(yùn)行模式,然后再講解一下其構(gòu)造方法相關(guān)形參的意義。
我個人把ThreadPoolExecutor理解成以下的模型,見下圖:


ThreadPool1

根據(jù)上面這個模型來理解構(gòu)造參數(shù)也就不難了。
int corePoolSize:設(shè)置核心線程的數(shù)量。
int maximumPoolSize:設(shè)置線程池最大線程容納量,當(dāng)活動線程數(shù)達(dá)到該值時(shí),后續(xù)新任務(wù)會被阻塞。
long keepAliveTime:設(shè)置非核心線程閑置的超時(shí)時(shí)長,當(dāng)allowCoreThreadTimeOut為True時(shí)同樣會影響到核心線程。
TimeUnit unit:指定KeepAliveTime參數(shù)的時(shí)間單位。
BlockingQueue<Runnable> workQueue:超額Runnable對象(參見模型圖)的儲存地。
ThreadFactory threadFactory:提供創(chuàng)建新線程的功能。
此外還有一個不常用的參數(shù)TejectedExecutionHandler handler,主要用于通知任務(wù)執(zhí)行失敗的情況,具體使用講解讀者自行學(xué)習(xí)。

ThreadPoolExecutor怎么執(zhí)行任務(wù)的呢?參見下面流程圖:


ThreadPoolExecutor Run

四大線程池

定義及模型

四大線程池的創(chuàng)建采用了靜態(tài)工廠模式(關(guān)于靜態(tài)工廠模式以后有相關(guān)的博文),我們來看看它們的定義及創(chuàng)建時(shí)關(guān)鍵的源碼,然后給出模型圖。

newCachedThreadPool創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。

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

newFixedThreadPool 創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊(duì)列中等待。

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

newScheduledThreadPool 創(chuàng)建一個定長線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
ThreadPool4

newSingleThreadExecutor 創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。

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

使用

對于使用方法直接在源碼中體現(xiàn),源碼已經(jīng)足夠通俗易懂,可以結(jié)合源碼及上面的定義模型分析學(xué)習(xí)。

public class ThreadPool {
    private final static int SCHEDULED_DELAY = 0;
    private final static int SCHEDULED_AT_FIXED_RATE = 1;
    public static void main(String args[]){
        startCachedThreadPool();
        startFixedThreadPool();
        startScheduledThreadPool(SCHEDULED_DELAY);
        startScheduledThreadPool(SCHEDULED_AT_FIXED_RATE);
        startSingleThreadExecutor();
    }
    private static void startCachedThreadPool(){
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        //可緩存的線程池
        for(int i = 0;i<10;i++){
            final int index = i;
            try {
                Thread.sleep(index * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(index);
                    System.out.println("Thread's ID: "+Thread.currentThread().getId());
                }
            });
        }
        //線程池為無限大,當(dāng)執(zhí)行第二個任務(wù)時(shí)第一個任務(wù)已經(jīng)完成,會復(fù)用執(zhí)行第一個任務(wù)的線程,而不用每次新建線程。
    }
    private static void startScheduledThreadPool(int status){
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        switch(status){
            case SCHEDULED_DELAY:
                scheduledThreadPool.schedule(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("delay 3 seconds");
                        System.out.println("Thread's ID: "+Thread.currentThread().getId());
                    }
                },3, TimeUnit.SECONDS);
                break;
            case SCHEDULED_AT_FIXED_RATE:
                scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("delay 1 seconds,and excute every 3 seconds");
                        System.out.println("Thread's ID: "+Thread.currentThread().getId());
                    }
                },1,3,TimeUnit.SECONDS);
                //不一定使用同一個線程來發(fā)送。
                break;
        }

    }
    private static void startFixedThreadPool(){
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        for(int i =0;i<10;i++){
            final int index = i;
            fixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(index);
                        System.out.println("Thread's ID: "+Thread.currentThread().getId());
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
    private static void startSingleThreadExecutor(){
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for(int i = 0;i<10;i++){
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(index);
                        System.out.println("Thread's ID: "+Thread.currentThread().getId());
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

總結(jié)

到此處博主大概梳理了一遍線程池的基本知識,如果有疏漏或者錯誤的地方求指正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容