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)部還保存著兩個整形變量,分別標(biāo)識著隊列的頭部和尾部在數(shù)組中的位置这橙;
  • LinkedBlockingQueue:基于鏈表的阻塞隊列奏窑,同ArrayListBlockingQueue類似,其內(nèi)部也維持著一個數(shù)據(jù)緩沖隊列(該隊列由一個鏈表構(gòu)成)析恋;
  • DelayQueue:DelayQueue 中的元素只有當(dāng)其指定的延遲時間到了良哲,才能夠從隊列中獲取到該元素。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í)行當(dāng)前任務(wù)辟犀;
  • DiscardPolicy:直接丟棄任務(wù);

線程池策略

(1)當(dāng) currentSize < corePoolSize 時耻蛇,直接啟動一個核心線程并執(zhí)行任務(wù)踪蹬。
(2)當(dāng) currentSize >= corePoolSize、并且 workQueue 未滿時臣咖,添加進來的任務(wù)會被安排到 workQueue 中等待執(zhí)行跃捣。
(3)當(dāng) workQueue 已滿,但是 currentSize < maximumPoolSize 時夺蛇,會立即開啟一個非核心線程來執(zhí)行任務(wù)疚漆。
(4)當(dāng) 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)建一個單線程的線程池狡耻。這個線程池只有一個線程在工作,也就是相當(dā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ù))的線程,當(dāng)任務(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)系作者
  • 序言:七十年代末蝇刀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子徘溢,更是在濱河造成了極大的恐慌吞琐,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件然爆,死亡現(xiàn)場離奇詭異站粟,居然都是意外死亡,警方通過查閱死者的電腦和手機曾雕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門奴烙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人剖张,你說我怎么就攤上這事切诀。” “怎么了搔弄?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵幅虑,是天一觀的道長。 經(jīng)常有香客問我顾犹,道長倒庵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任蹦渣,我火速辦了婚禮哄芜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柬唯。我一直安慰自己认臊,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布锄奢。 她就那樣靜靜地躺著失晴,像睡著了一般剧腻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涂屁,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天书在,我揣著相機與錄音,去河邊找鬼拆又。 笑死儒旬,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帖族。 我是一名探鬼主播栈源,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼竖般!你這毒婦竟也來了甚垦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤涣雕,失蹤者是張志新(化名)和其女友劉穎艰亮,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挣郭,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡迄埃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了丈屹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片调俘。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖旺垒,靈堂內(nèi)的尸體忽然破棺而出彩库,到底是詐尸還是另有隱情,我是刑警寧澤先蒋,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布骇钦,位于F島的核電站,受9級特大地震影響竞漾,放射性物質(zhì)發(fā)生泄漏眯搭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一业岁、第九天 我趴在偏房一處隱蔽的房頂上張望鳞仙。 院中可真熱鬧,春花似錦笔时、人聲如沸棍好。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽借笙。三九已至扒怖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間业稼,已是汗流浹背盗痒。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留低散,地道東北人俯邓。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像熔号,于是被迫代替她去往敵國和親看成。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

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