
一、Android線程池介紹
1.1 原理
Android中的線程池概念來源于Java中的Executor,Executor是一個接口,真正的線程的實現(xiàn)為ThreadPoolExecutor。(ThreadPoolExecutor繼承了AbstractExecutorService,AbstractExecutorService是ExecutorService的實現(xiàn)類,ExecutorService繼承了Executor接口)。
1.2 分類
從線程池的功能特性上來說,Android中線程池主要分為4類:
- FixedThreadPool(線程數(shù)量固定)、
- CachedThreadPool(線程數(shù)量不定)、
- ScheduledThreadPool(核心線程數(shù)量固定,非核心線程數(shù)量無限制)、
- SingleThreadExecutor(只有一個核心線程,所有任務在里面按順序執(zhí)行)。
1.3 優(yōu)點
- 重用線程池中的線程,避免頻繁創(chuàng)建和銷毀線程所帶來的內存開銷。
- 有效控制線程的最大并發(fā)數(shù),避免因線程之間搶占資源而導致的阻塞現(xiàn)象。
- 能夠對線程進行簡單的管理,提供定時執(zhí)行以及指定時間間隔循環(huán)執(zhí)行等功能。
二、 Android線程池分類
由于Android中的線程池都是直接或間接通過配置ThreadPoolExecutor來實現(xiàn)的,因此在介紹它們之前要先介紹ThreadPoolExecutor。
2.1 ThreadPoolExecutor介紹
ThreadPoolExecutor有多個構造方法以及參數(shù),下面介紹比較常用的構造方法。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
- corePoolSize:線程池的核心線程數(shù),默認情況下核心線程會一直存活于線程池,即使處于閑置狀態(tài)。如果設置了ThreadPoolExecutor里的allowCoreThreadTimeOut為True,則核心線程在等待新任務到來時會有超時終止策略。超時的時間間隔由keepAliveTime所決定。
private volatile long keepAliveTime;
private volatile boolean allowCoreThreadTimeOut;
- maximumPoolSize:線程池所能容納的最大線程數(shù),當線程數(shù)達到這個數(shù)值后,后續(xù)的新任務將會被阻塞。
- keepAliveTime:非核心線程超時時長,超過這個時間沒有任務執(zhí)行,非核心線程就會被回收。當核心線程設置allowCoreThreadTimeOut為true時,keepAliveTime同樣作用于核心線程。
-
unit:用于指定keepAliveTime參數(shù)的時間單位,這是一個枚舉
,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECOUNDS(秒)以及TimeUnit.MINUTS(分鐘)等,并且每個變量提供了轉換函數(shù)方便各個單位數(shù)據(jù)的轉換。 - workQueue:線程池中的任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這個參數(shù)中。
-
threadFactory:線程工廠,為線程池提供創(chuàng)建新線程的功能。ThreadFactory是一個接口,它只有一個方法:
Thread newThread(Runnable r); -
handler:該handler的類型為RejectedExecutionHandler。這個參數(shù)不常用,它的作用是當線程池無法執(zhí)行新任務時,會調用handler的
rejectedExecution(Runnable r, ThreadPoolExecutor e)方法來拋出異常。無法執(zhí)行的原因可能是由于隊列滿或者任務無法成功執(zhí)行等。
ThreadPoolExecutor執(zhí)行任務大致遵循以下規(guī)則:
(1)如果線程池中的線程數(shù)量未達到核心線程的數(shù)量,會直接啟動一個核心線程來執(zhí)行任務。
(2)如果線程池中的線程數(shù)量已經(jīng)達到或者超過核心線程的數(shù)量,那么任務會被插入到任務隊列中排隊等待執(zhí)行。
(3)如果第2步中無法插入新任務,說明任務隊列已滿,如果未達到規(guī)定的最大線程數(shù)量,則啟動一個非核心線程來執(zhí)行任務。
(4)如果第3步中線程數(shù)量超過規(guī)定的最大值,則拒絕任務并使用RejectedExecutionHandler的rejectedExecution(Runnable r, ThreadPoolExecutor e)方法來通知調用者。
2.2 線程池的分類
先介紹一個類Executors,這個類定義了各種參數(shù)用于創(chuàng)建多種線程池,最終實現(xiàn)還是通過ThreadPoolExecutor類。
1. FixedThreadPool (Fixed:固定的,不變的)
通過Executors的newFixedThreadPool創(chuàng)建,通過創(chuàng)建時的參數(shù)可以看出又以下幾個特點:
- 線程數(shù)量固定且都是核心線程:核心線程數(shù)量和最大線程數(shù)量都是nThreads;
- 都是核心線程且不會被回收,快速相應外界請求;
- 沒有超時機制,任務隊列也沒有大小限制;
- 新任務使用核心線程處理,如果沒有空閑的核心線程,則排隊等待執(zhí)行。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2. CachedThreadPool (Cached:緩存)
通過Executors的newCachedThreadPool創(chuàng)建,特點:
- 線程數(shù)量不定,只有非核心線程,最大線程數(shù)任意大:傳入核心線程數(shù)量的參數(shù)為0,最大線程數(shù)為Integer.MAX_VALUE;
- 有新任務時使用空閑線程執(zhí)行,沒有空閑線程則創(chuàng)建新的線程來處理。
- 該線程池的每個空閑線程都有超時機制,時常為60s(參數(shù):60L, TimeUnit.SECONDS),空閑超過60s則回收空閑線程。
- 適合執(zhí)行大量的耗時較少的任務,當所有線程閑置超過60s都會被停止,所以這時幾乎不占用系統(tǒng)資源。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3. ScheduledThreadPool(Scheduled:預定的、排定的)
通過Executors的newScheduledThreadPool創(chuàng)建,特點:
- 核心線程數(shù)量固定,非核心線程數(shù)量無限制;
- 非核心線程閑置超過10s會被回收;
- 主要用于執(zhí)行定時任務和具有固定周期的重復任務;
- 四個里面唯一一個有延時執(zhí)行和周期重復執(zhí)行的功能:創(chuàng)建時
ScheduledThreadPoolExecutor(corePoolSize)返回的是new ScheduledThreadPoolExecutor對象,ScheduledThreadPoolExecutor是ThreadPoolExecutor的子類,DelayedWorkQueue是ScheduledThreadPoolExecutor的一個靜態(tài)內部類,主要用于處理任務隊列延遲的工作。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//默認閑置超時回收時常
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
4. SingleThreadExecutor (單線程線程池)
通過Executors的newSingleThreadExecutor創(chuàng)建,特點:
- 只有一個核心線程,所有任務在同一個線程按順序執(zhí)行。
- 所有的外界任務統(tǒng)一到一個線程中,所以不需要處理線程同步的問題。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
三、 Android線程池簡單使用
3.1 線程池簡單使用
(1)上面所說的四種常用線程池的實例化:
//創(chuàng)建一個Runnable對象
Runnable runnable = new Runnable() {
@Override
public void run() {
// do something
}
};
//四種線程池執(zhí)行Runnable對象
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);
fixedThreadPool.execute(runnable);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(runnable);
// 注意這里創(chuàng)建的是ScheduledExecutorService對象,ScheduledExecutorService是ExecutorService的子類
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
// 1000ms后執(zhí)行runnable
scheduledThreadPool.schedule(runnable,1000,TimeUnit.MILLISECONDS);
// 1000ms后,每3000ms執(zhí)行一次runnable
scheduledThreadPool.scheduleAtFixedRate(runnable,1000,2000,TimeUnit.MILLISECONDS);
isRunning = true;
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(runnable);
(2)簡單使用小demo:
添加線程池開始執(zhí)行和ScheduledThreadPool停止執(zhí)行的兩個按鈕點擊事件,布局就不貼了。
findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
count = 0;
mStatueText.setText("線程開始執(zhí)行,次數(shù):"+ count);
startThreadPool();
}
});
findViewById(R.id.btn_stop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(null != scheduledThreadPool && isRunning){
scheduledThreadPool.shutdown();
mStatueText.setText("scheduledThreadPool線程停止,當前次數(shù):"+ count);
isRunning = false;
}
}
});
startThreadPool();方法里Runnable每執(zhí)行一次增加一次count并打到TextView上:
Runnable runnable = new Runnable() {
@Override
public void run() {
count++;
runOnUiThread(new Runnable() {
@Override
public void run() {
mStatueText.setText("線程執(zhí)行完畢,次數(shù):"+ count);
}
});
}
};
接著讓所有的線程池執(zhí)行這個Runnable對象,最后的結果是變量count的值從0直接到了3,然后又到5最后無限增長。
原因是除ScheduledThreadPool對象外的三個線程池很快執(zhí)行了任務,ScheduledThreadPool對象的執(zhí)行了兩次任務,兩個都延時1000ms。最后循環(huán)執(zhí)行的任務一直在增加count的值。
相關文章:
Android多線程:理解和簡單使用總結