Android線程池淺析

引言

在Android開(kāi)發(fā)中,只要是耗時(shí)的操作都需要開(kāi)啟一個(gè)線程來(lái)執(zhí)行。例如網(wǎng)絡(luò)訪問(wèn)必須放到子線程中執(zhí)行,否則會(huì)拋異常(NetworkOnMainThreadException),這樣做的目的也是為了防止用戶在主線程中做耗時(shí)操作,這樣很容易引起ANR。那么有了線程為什么還需要線程池呢?

線程的創(chuàng)建過(guò)程分為3步

  • 創(chuàng)建線程 T1
  • 執(zhí)行線程 T2
  • 銷毀線程 T2

線程創(chuàng)建的總時(shí)間T=T1+T2+T3。 可以看出T1,T3是多線程本身的帶來(lái)的開(kāi)銷,我們渴望減少T1,T3所用的時(shí)間,從而減少T的時(shí)間。但一些線程的使用者并沒(méi)有注意到這一點(diǎn),所以在程序中頻繁的創(chuàng)建或銷毀線程,這導(dǎo)致T1和T3在T中占有相當(dāng)比例。顯然這是突出了線程的弱點(diǎn)(T1,T3),而不是優(yōu)點(diǎn)(并發(fā)性)。

合理利用線程池能夠帶來(lái)三個(gè)好處。

  1. 降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
  2. 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
  3. 提高線程的可管理性。線程是稀缺資源,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。但是要做到合理的利用線程池,必須對(duì)其原理了如指掌。

線程池的使用

首先我們來(lái)看下Java中線程池的使用。

Executor

Java中線程池基于Executor接口。Executor 接口中定義了一個(gè)方法 void execute(Runnable command),該方法接收一個(gè) Runable 實(shí)例,它用來(lái)執(zhí)行一個(gè)任務(wù),任務(wù)即一個(gè)實(shí)現(xiàn)了 Runnable 接口的類。

ExecutorService

ExecutorService 繼承自 Executor 接口,它提供了更豐富的實(shí)現(xiàn)多線程的方法。

  • shutdown()方法來(lái)平滑地關(guān)閉 ExecutorService,調(diào)用該方法后,將導(dǎo)致 ExecutorService 停止接受任何新的任務(wù)且等待已經(jīng)提交的任務(wù)執(zhí)行完成(已經(jīng)提交的任務(wù)會(huì)分兩類:一類是已經(jīng)在執(zhí)行的,另一類是還沒(méi)有開(kāi)始執(zhí)行的),當(dāng)所有已經(jīng)提交的任務(wù)執(zhí)行完畢后將會(huì)關(guān)閉 ExecutorService。因此我們一般用該接口來(lái)實(shí)現(xiàn)和管理多線程。
  • submit(Runnable task)方法與Executor的execute()方法效果一樣,但是submit有返回值,如果我希望task執(zhí)行完后,每個(gè)task告訴我它的執(zhí)行結(jié)果,是成功還是失敗,如果是失敗,原因是什么,這時(shí)候就需要用到submit,通過(guò)返回的Future來(lái)獲取到結(jié)果

ThreadPoolExecutor

ThreadPoolExecutor 是正真線程池的實(shí)現(xiàn)類,我們創(chuàng)建線程的時(shí)候一般也是使用這個(gè)類。先看下構(gòu)造方法

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize 線程池的核心線程數(shù),也可以理解為最小線程數(shù),默認(rèn)情況下,核心線程在線程池中是一直存活的,即使當(dāng)前執(zhí)行的線程數(shù)量小于核心線程數(shù)量,所有的核心線程均會(huì)被創(chuàng)建出來(lái)。如果將ThreadPoolExecutor的allowCoreThreadTimeOut設(shè)置為ture,那么核心線程就會(huì)有超時(shí)策略。
  • maximumPoolSize 線程池所能容納的最大線程數(shù),包括核心和非核心線程,當(dāng)活動(dòng)線程到達(dá)最大值,后續(xù)的任務(wù)就會(huì)被阻塞。
  • keepAliveTime 非核心線程閑置時(shí)的超時(shí)時(shí)長(zhǎng),當(dāng)非核心線程空閑時(shí)間超過(guò)這個(gè)值,就會(huì)被終止。如果ThreadPoolExecutor的allowCoreThreadTimeOut設(shè)置為true,這個(gè)時(shí)間同樣會(huì)作用在核心線程上;
  • unit 時(shí)間單位
  • workQueue 線程池中的任務(wù)隊(duì)列,所有提交的Runable對(duì)象會(huì)存儲(chǔ)在這個(gè)參數(shù)中
  • threadFactory 創(chuàng)建新線程時(shí)使用的factory
  • handler 當(dāng)task出現(xiàn)異常時(shí),會(huì)通過(guò)這個(gè)handler通知外界

那么線程池針對(duì)不同的線程數(shù)量又是怎么執(zhí)行的呢,主要可以分為以下幾種情況

  • 線程數(shù) < corePoolSize 所有核心線程都會(huì)用來(lái)處理任務(wù)。
  • 線程數(shù) = corePoolSize 緩沖隊(duì)列workQueue未滿,任務(wù)被放入緩沖隊(duì)列。
  • corePoolSize < 線程數(shù) < maximumPoolSize 緩沖隊(duì)列workQueue滿,創(chuàng)建新的線程來(lái)處理被添加的任務(wù)。
  • corePoolSize < maximumPoolSize < 線程數(shù) 緩沖隊(duì)列workQueue滿,通過(guò) handler所指定的策略來(lái)處理此任務(wù)。

處理任務(wù)的優(yōu)先級(jí)為:核心線程corePoolSize、任務(wù)隊(duì)列workQueue、最大線程maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務(wù)。 handler又有以下四種選擇

  • CallerRunsPolicy 再次添加該task并執(zhí)行execute方法
  • AbortPolicy 放棄該task并拋出RejectedExecutionException
  • DiscardPolicy 放棄當(dāng)前task
  • DiscardOldestPolicy 放棄等待時(shí)間最久的task

線程池的進(jìn)一步封裝

看了ThreadPoolExecutor的簡(jiǎn)單介紹是不是覺(jué)得使用過(guò)程比較復(fù)雜呢,沒(méi)關(guān)系,Java針對(duì)這一點(diǎn)還可以通過(guò)Executors的工廠方法配置,handler均使用的是defaultHandler即AbortPolicy只要線程數(shù)量大于核心線程數(shù)就會(huì)拋出異常需要自行處理。主要有以下五種:

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

核心線程數(shù)=最大線程數(shù),并且沒(méi)有超時(shí)策略。也就是說(shuō)該線程池沒(méi)有空閑狀態(tài),能最快速度響應(yīng)。

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

核心線程數(shù)=最大線程數(shù)=1,沒(méi)有超時(shí)策略。所有任務(wù)在一個(gè)線程中處理,不存在同步問(wèn)題。handler為AbortPolicy。

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

核心線程=0 ,最大線程=Integer.MAX_VALUE,這就意味著沒(méi)有線程數(shù)量的限制,超時(shí)時(shí)間為60毫秒。new SynchronousQueue<Runnable>這個(gè)任務(wù)隊(duì)列比較特殊,簡(jiǎn)單理解為一個(gè)無(wú)法存儲(chǔ)的任務(wù)隊(duì)列。也就是說(shuō),當(dāng)有新的任務(wù)到來(lái),如果有空閑的線程,則將任務(wù)給空閑的線程處理,否則直接創(chuàng)建一個(gè)新的線程來(lái)處理任務(wù)。也就是說(shuō)一旦有任務(wù)來(lái)就會(huì)立刻執(zhí)行,但是消耗較大,所以比較適合用來(lái)處理大量的耗時(shí)較短的任務(wù)。

  • newScheduledThreadPool
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
            new DelayedWorkQueue());
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
}

核心線程為自定義 ,最大線程=Integer.MAX_VALUE,超時(shí)時(shí)間為10毫秒,也就是說(shuō)一旦線程空閑便立即回收。適用于定時(shí)任務(wù)與周期性任務(wù)。

  • newWorkStealingPool (Java8中新引入)
public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
}

這個(gè)線程池和其它四個(gè)不太一樣,是Java8中新引入的。線程數(shù)默認(rèn)為主機(jī)CPU的可用核心數(shù),且會(huì)動(dòng)態(tài)的增加與減少,提交的任務(wù)執(zhí)行順序并不能得到保證。

Android中的線程池

了解了Java提供的線程池我們就來(lái)看下Android中是怎樣使用線程池的。

AsyncTask是Android中異步處理的輕量級(jí)框架,其中的實(shí)現(xiàn)就是一個(gè)線程池。

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
        }
    };

    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                postResultIfNotInvoked(get());
            } catch (InterruptedException e) {
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e) {
                throw new RuntimeException("An error occurred while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };
}

在構(gòu)造方法中初始化了兩個(gè)變量mWorker與mFuture,這兩個(gè)變量很重要

  • mWorker是一個(gè)WorkerRunnable
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

WorkerRunnable是一個(gè)實(shí)現(xiàn)了Callable接口的類。

public interface Callable<V> {
    V call() throws Exception;
}

Callable接口里面只定義了一個(gè)call方法,返回一個(gè)泛型對(duì)象。那么Callable到底有什么用呢?線程無(wú)論繼承Thread類還是實(shí)現(xiàn)Runnable方法,執(zhí)行完任務(wù)以后都無(wú)法直接返回結(jié)果。而Callable接口就彌補(bǔ)了這個(gè)缺陷,當(dāng)call方法執(zhí)行完畢以后會(huì)返回一個(gè)泛型對(duì)象。WorkerRunnable實(shí)現(xiàn)了Callable接口,也就是說(shuō)我們可以調(diào)用mWorker.call()來(lái)獲取返回的結(jié)果

  • mFuture是一個(gè)FutureTask
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

參數(shù)是一個(gè)Callable,也就是mWorker。FutureTask實(shí)現(xiàn)了RunnableFuture接口。

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

這個(gè)接口又繼承了Runable與Future<V>。
Future<V>接口主要是針對(duì)Runnable和Callable任務(wù)的。
提供了三種功能:

  1. 判斷任務(wù)是否完成 boolean isDone()
  2. 能夠中斷任務(wù) boolean cancel(boolean mayInterruptIfRunning);
  3. 能夠獲取任務(wù)執(zhí)行的結(jié)果 V get();

也就是說(shuō)FutureTask既能夠被線程執(zhí)行,又能提供線程執(zhí)行任務(wù)后返回的結(jié)果。

看完了構(gòu)造方法我們從execute()入手

public final AsyncTask<Params, Progress, Result> execute(Params... params)
{
    return executeOnExecutor(sDefaultExecutor, params);
}

在執(zhí)行execute方法時(shí)傳入了初始化時(shí)的參數(shù)與sDefaultExecutor

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

sDefaultExecutor就是一個(gè)實(shí)現(xiàn)了Executor的基本類,execute中,在一個(gè)雙端隊(duì)列中不斷的插入Runable對(duì)象。ArrayDeque的內(nèi)部是使用數(shù)組形式來(lái)實(shí)現(xiàn)雙端隊(duì)列的,我們知道隊(duì)列是FIFO的,只能在隊(duì)頭刪除元素,隊(duì)尾添加元素,而雙端隊(duì)列是在隊(duì)頭和隊(duì)尾都能夠刪除和添加元素。需要注意的是ArrayDeque沒(méi)有容量的限制,隊(duì)列滿了以后會(huì)自動(dòng)進(jìn)行擴(kuò)充。

THREAD_POOL_EXECUTOR則是AsyncTask中的關(guān)鍵線程池

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

public static final Executor THREAD_POOL_EXECUTOR;

private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
            
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

AsyncTask在靜態(tài)代碼塊中初始化了一個(gè)ThreadPoolExecutor,核心線程數(shù)=[2,4/CPU核心數(shù)-1],最大線程數(shù)=CPU核心數(shù)*2+1,超時(shí)時(shí)間為30秒,隊(duì)列長(zhǎng)度為128,handler為AbortPolicy。并且設(shè)置了allowCoreThreadTimeOut(true),即核心線程超時(shí)可被回收。

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

在Asynck調(diào)用execute()時(shí),mWorker獲取到參數(shù),sDefaultExecutor開(kāi)始執(zhí)行execute方法。

public synchronized void execute(final Runnable r) {
    mTasks.offer(new Runnable() {
        public void run() {
            try {
                r.run();
            } finally {
                scheduleNext();
            }
        }
    });
    if (mActive == null) {
        scheduleNext();
    }
}

protected synchronized void scheduleNext() {
    if ((mActive = mTasks.poll()) != null) {
        THREAD_POOL_EXECUTOR.execute(mActive);
    }
}

回過(guò)來(lái)看,第一次執(zhí)行時(shí),mActive=null這時(shí)候便會(huì)執(zhí)行scheduleNext(),當(dāng)隊(duì)列中有數(shù)據(jù)的時(shí)候便會(huì)把數(shù)據(jù)取出來(lái)放進(jìn)線程池中執(zhí)行。在執(zhí)行THREAD_POOL_EXECUTOR.execute(mActive)時(shí),因?yàn)閙Future實(shí)現(xiàn)了Runable接口,這個(gè)時(shí)候便會(huì)調(diào)用run方法

public void run() {
    if (state != NEW ||
        !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

其中的callable就是mWorker,此時(shí)會(huì)調(diào)用mWorker的call()方法

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //noinspection unchecked
        return postResult(doInBackground(mParams));
    }
};

這個(gè)就是在構(gòu)造方法中創(chuàng)建的,在call中實(shí)現(xiàn)了doInBackground方法,并把返回值給了postResult,postResult中的邏輯就是獲取一個(gè)Message,然后發(fā)送該Message給Handler處理。

簡(jiǎn)單回顧下,AsyncTask每次將doInBackground()中的操作添加到線程池中執(zhí)行,執(zhí)行完后由handler傳遞數(shù)據(jù)。

總結(jié)

在Android開(kāi)發(fā)中對(duì)于線程池的使用可能僅僅只是一些異步的框架中有封裝,我們很少真正使用,僅僅new一個(gè)Thread,對(duì)于一些頻繁的請(qǐng)求與周期性的操作不如嘗試試用下線程池。

舉個(gè)例子:
我們需要每隔10秒進(jìn)行某個(gè)操作,這時(shí)你可能會(huì)考慮用一個(gè)定時(shí)器,每次開(kāi)一個(gè)線程去請(qǐng)求,這樣T1與T2的時(shí)間開(kāi)銷是很大的,此時(shí)使用線程池便能大大優(yōu)化性能。

如有錯(cuò)誤的地方歡迎大家留言指正探討。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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