Android 多線程和線程池

Android 多線程的可以歸納為兩種情況:
1、將任務(wù)從工作線程拋到主線程;
2、將任務(wù)從主線程拋到工作線程;

一、將任務(wù)從工作線程拋到主線程

1、Handler#sendXXXMessage 方法

sendXXXMessage 方法共有七個:

public final boolean sendMessage(Message msg)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessageAtFrontOfQueue(Message msg)

2、Handler#postRunnable(Runnable)方法

postXXX 系列方法有四個

public final boolean post(Runnable r)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postAtFrontOfQueue(Runnable r)

其實內(nèi)部都是調(diào)用 getPostMessage 把 Runnable 封裝成 Message 對象的 callback 屬性,然后調(diào)用 sendXXXMessage 系列方法

3、Activity.runOnUIThread(Runnable)方法

public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

如果 Activity 在 UI 線程,直接運行該 Runnable 對象的 run 方法,如果不在 UI 線程,通過 Activity 持有的 Handle 對象調(diào)用 post 方法

4、View.post(Runnable)方法

public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

5、AyscTask

@MainThread
protected void onPreExecute() {}
@MainThread
protected void onPostExecute(Result result) {}
@MainThread
protected void onProgressUpdate(Progress... values) {}

二、將任務(wù)從主線程拋到工作線程

1、Thread,Runnable

繼承 Thread 或者實現(xiàn) Runnable 接口

2、AyscTask

@WorkerThread
protected abstract Result doInBackground(Params... params);

3、HandlerThread

該類繼承自 Thread,在普通的線程中是沒有 Looper 對象的,在該線程的 run 方法中調(diào)用 Looper.prepare()Looper.loop() 開啟消息循環(huán),這樣就允許在 HandlerThread 中創(chuàng)建 Handler 了。

HandleThread#run

一般的使用步驟是:
創(chuàng)建一個 HandlerThread 對象并調(diào)用 HandlerThraed#start() 方法啟動線程,然后調(diào)用 HandlerThread#getLooper() 獲取 Looper 對象作為參數(shù)創(chuàng)建 Handler 對象

4、IntentService

該類繼承自 Service 類,在 onCreate 方法中開啟了一個 HandlerThread ,并把該線程的 Looper 對象作為參數(shù)創(chuàng)建一個 Handler 對象:


IntentService#onCreate

內(nèi)部定義了 Handler 的子類 ServiceHandler,在 handleMessage 方法中回調(diào) onHandleIntent 方法,所以在使用 IntentService 時在 onHandleIntent 方法中處理耗時操作


ServiceHandler

三、線程池

什么時候使用線程池
  • 單個任務(wù)處理時間比較短
  • 需要處理的任務(wù)數(shù)量很大
使用線程池的優(yōu)點:
  • 重用線程池中的線程,避免線程的創(chuàng)建和銷毀帶來的性能開銷。
  • 可以有效控制線程池中的最大并發(fā)數(shù),避免大量線程之間互相搶占系統(tǒng)資源導(dǎo)致阻塞;
  • 能夠?qū)€程進行簡單的管理并提供定時執(zhí)行、間隔執(zhí)行等功能。

線程池相關(guān)類

  • Executor :Java 中線程池的頂級接口;
  • ExecutorService:真正的線程池接口;
  • ScheduledExecutorService:和Timer/TimerTask類似,解決那些需要任務(wù)重復(fù)執(zhí)行的問題;
  • ThreadPoolExecutor:ExecutorService 的默認實現(xiàn);
  • ScheduledThreadPoolExecutor:繼承 ThreadPoolExecutor 的 ScheduledExecutorService 接口實現(xiàn),周期性任務(wù)調(diào)度的類實現(xiàn);
  • Executors:創(chuàng)建一些常見的線程池;

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,//核心池的大小
                              int maximumPoolSize,//線程池最大線程數(shù)
                              long keepAliveTime,//保持時間
                              TimeUnit unit,//時間單位
                              BlockingQueue<Runnable> workQueue,//任務(wù)隊列
                              ThreadFactory threadFactory,//線程工廠
                              RejectedExecutionHandler handler) //異常的捕捉器
}

相關(guān)參數(shù):

  • corePoolSize:核心線程數(shù)。提交任務(wù)時,如果線程池內(nèi)的線程數(shù)小于 corePoolSize,無論有沒有空閑線程都會創(chuàng)建幾個新的線程,如果調(diào)用了 ThreadPoolExecutor#prestartCoreThread() 線程池會提前創(chuàng)建并開啟所有核心線程;
  • maximumPoolSize:最大線程數(shù)。在提交任務(wù)時,如果任務(wù)隊列已滿,并且線程池內(nèi)的線程數(shù)小于最大線程數(shù),則線程池會創(chuàng)建新的線程;
  • keepAliveTime:線程池維護線程所允許的空閑時間。一般情況下用于非核心線程,只有 ThreadPoolExecutor#allowCoreTheadTimeOut() 設(shè)為 true 時才作用于核心線程;
  • unit:超時時間的單位。是一個枚舉值,TimeUnit.Days、TimeUnit.HOURS、TimeUnit.MINUTES、TiemUnit.SECONDS、TimeUnit.MILLISECONDS 等;
  • workQueue:等待隊列,提交任務(wù)時,如果線程池內(nèi)的線程數(shù)大于等于核心線程數(shù),那么會把該任務(wù)封裝成一個 Worker 對象添加到等待隊列;
    該參數(shù)是 BlockingQueue 接口的實現(xiàn)類,常見的 BlockingQueue 類有:
  • ArrayBlockingQueue:基于數(shù)組的阻塞隊列實現(xiàn),在 ArrayBlockingQueue 內(nèi)部,維護了一個定長數(shù)組,以便緩存隊列中的數(shù)據(jù)對象,這是一個常用的阻塞隊列,除了一個定長數(shù)組外,
    ArrayBlockingQueue 內(nèi)部還保存著兩個整形變量,分別標識著隊列的頭部和尾部在數(shù)組中的位置;
  • LinkedBlockingQueue:基于鏈表的阻塞隊列,同ArrayListBlockingQueue類似,其內(nèi)部也維持著一個數(shù)據(jù)緩沖隊列(該隊列由一個鏈表構(gòu)成);
  • DelayQueue:DelayQueue 中的元素只有當其指定的延遲時間到了,才能夠從隊列中獲取到該元素。DelayQueue 是一個沒有大小限制的隊列,因此往隊列中插入數(shù)據(jù)的操作(生產(chǎn)者)永遠不會被阻塞,而只有獲取數(shù)據(jù)的操作(消費者)才會被阻塞。
  • PriorityBlockingQueue:基于優(yōu)先級的阻塞隊列(優(yōu)先級的判斷通過構(gòu)造函數(shù)傳入的 Compator 對象來決定),但需要注意的是
    PriorityBlockingQueue 并不會阻塞數(shù)據(jù)生產(chǎn)者,而只會在沒有可消費的數(shù)據(jù)時,阻塞數(shù)據(jù)的消費者。因此使用的時候要特別注意,生產(chǎn)者生產(chǎn)數(shù)據(jù)的速度絕對不能快于消費者消費數(shù)據(jù)的速度,否則時間一長,會最終耗盡所有的可用堆內(nèi)存空間。
  • SynchronousQueue: 一種無緩沖的等待隊列;
  • threadFactory:ThreadFactory 類型的變量,用于創(chuàng)建新線程;
  • handler:RejectedExecutionHandler 類型的變量,表示線程池的飽和策略。如果阻塞隊列滿了并且沒有空閑的線程,這時如果繼續(xù)提交任務(wù),就需要采取一種策略處理該任務(wù)。線程池提供了4種策略:
  • AbortPolicy:直接拋出異常,這是默認策略;
  • CallerRunsPolicy:用調(diào)用者所在的線程來執(zhí)行任務(wù);
  • DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務(wù),并執(zhí)行當前任務(wù);
  • DiscardPolicy:直接丟棄任務(wù);

線程池策略

(1)當 currentSize < corePoolSize 時,直接啟動一個核心線程并執(zhí)行任務(wù)。
(2)當 currentSize >= corePoolSize、并且 workQueue 未滿時,添加進來的任務(wù)會被安排到 workQueue 中等待執(zhí)行。
(3)當 workQueue 已滿,但是 currentSize < maximumPoolSize 時,會立即開啟一個非核心線程來執(zhí)行任務(wù)。
(4)當 currentSize >= corePoolSize、workQueue 已滿、并且
currentSize > maximumPoolSize 時,調(diào)用 handler 默認拋出
RejectExecutionExpection 異常。

主要方法
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
 
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

工作線程數(shù)小于 核心線程數(shù),調(diào)用 addWorker 方法創(chuàng)建一個新的線程
線程池中的每一個線程被封裝成一個 Worker 對象,ThreadPool 維護的其實就是一組 Worker 對象。

public void execute(Runnable command)//提交任務(wù)
public void shutdown()//正在執(zhí)行任務(wù)的線程執(zhí)行完后關(guān)閉
public List<Runnable> shutdownNow()//立即關(guān)閉大部分線程

Executors

提供了一些靜態(tài)方法,幫助我們方便的生成一些常用的線程池:
1)newSingleThreadExecutor

newSingleThreadExecutor

創(chuàng)建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當于單線程串行執(zhí)行所有任務(wù)。如果這個唯一的線程因為異常結(jié)束,那么會有一個新的線程來替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。
2)newFixedThreadPool

newFixedThreadPool

創(chuàng)建固定大小的線程池。每次提交一個任務(wù)就創(chuàng)建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執(zhí)行異常而結(jié)束,那么線程池會補充一個新線程。
3)newCachedThreadPool

newCachedThreadPool

創(chuàng)建一個可緩存的線程池。如果線程池的大小超過了處理任務(wù)所需要的線程,
那么就會回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當任務(wù)數(shù)增加時,此線程池又可以智能的添加新線程來處理任務(wù)。此線程池不會對線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。
4)newScheduledThreadPool

newScheduledThreadPool

創(chuàng)建一個大小無限的線程池。此線程池支持定時以及周期性執(zhí)行任務(wù)的需求。
ScheduledThreadPoolExecutor 有一系列的 scheduleXXX 方法:

public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit)
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay,TimeUnit unit) 
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit)

execute 方法和 submit 方法內(nèi)部都是調(diào)用了 schedule 方法

public void execute(Runnable command) {
        schedule(command, 0, NANOSECONDS);
    }
public Future<?> submit(Runnable task) {
        return schedule(task, 0, NANOSECONDS);
    }

public <T> Future<T> submit(Runnable task, T result) {
        return schedule(Executors.callable(task, result), 0, NANOSECONDS);
    }

public <T> Future<T> submit(Callable<T> task) {
        return schedule(task, 0, NANOSECONDS);
    }

線程池管理類

定義一個線程池管理類

public class ThreadManager {

    private ThreadPoolProxy longPool;
    private ThreadPoolProxy shortPool;

    private ThreadManager() {

    }

    private static ThreadManager instance = new ThreadManager();

    public static ThreadManager getInstance() {
        return instance;
    }

    public ThreadPoolProxy createLongThreadPool() {
        if (longPool == null) {
            longPool = new ThreadPoolProxy(5, 5, 5000);
        }
        return longPool;
    }

    public ThreadPoolProxy createShortThreadPool() {
        if (shortPool == null) {
            shortPool = new ThreadPoolProxy(3, 3, 5000);
        }
        return shortPool;
    }


    public class ThreadPoolProxy {
        private ThreadPoolExecutor executor;
        private int corePoolSize;
        private int maximumPoolSize;
        private long time;

        public ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long time) {
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.time = time;
        }

        public void execute(Runnable runnable) {
            if (executor == null) {
                executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, time, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<Runnable>(10));
            }
            executor.execute(runnable);
        }

        public void cancel(Runnable runnable) {
            if (executor != null && !executor.isShutdown() && !executor.isTerminated())
                executor.remove(runnable);
        }
    }
}

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

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

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