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 了般婆。
一般的使用步驟是:
創(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 對象:
內(nèi)部定義了 Handler 的子類 ServiceHandler,在 handleMessage 方法中回調(diào) onHandleIntent 方法页响,所以在使用 IntentService 時在 onHandleIntent 方法中處理耗時操作
三篓足、線程池
什么時候使用線程池
- 單個任務(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
創(chuàng)建一個單線程的線程池狡耻。這個線程池只有一個線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)猴凹。如果這個唯一的線程因為異常結(jié)束夷狰,那么會有一個新的線程來替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行郊霎。
2)newFixedThreadPool
創(chuàng)建固定大小的線程池沼头。每次提交一個任務(wù)就創(chuàng)建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變进倍,如果某個線程因為執(zhí)行異常而結(jié)束土至,那么線程池會補充一個新線程。
3)newCachedThreadPool
創(chuàng)建一個可緩存的線程池背捌。如果線程池的大小超過了處理任務(wù)所需要的線程毙籽,
那么就會回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時毡庆,此線程池又可以智能的添加新線程來處理任務(wù)。此線程池不會對線程池大小做限制烙如,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小么抗。
4)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);
}
}
}