在操作系統中,線程是操作系統調度的最小單位,同時線程又是一種受限的系統資源,即線程不可能無限地產生,并且線程的創建和銷毀都會有相應的開銷。所以就有了線程池的引入,它可以避免因為頻繁創建和銷毀線程所帶來的系統開銷。Android
中的線程來源于java
,主要是通過Executor
來派生特定的線程池。
優點:
(1).重用線程池中的線程,避免因為線程的創建和銷毀所帶來的性能開銷。
(2).能有效地控制線程池的最大并發數,避免大量的線程之間因互相搶占系統資源而導致的阻塞現象。
(3).能夠對線程進行簡單的管理,并提供定時執行以及指定間隔循環執行等功能。
Executor
接口的真正實現為ThreadPoolExecutor
,通過配置它的參數可以創建各種線程池。在介紹線程池的種類之前,先需要介紹下該類中每個參數的作用。
ThreadPoolExecutor
它有幾種構造函數提供我們實例化,看它參數最多的構造方法
//七個參數的構造函數
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- 1.核心線程 corePoolSize
線程池新建線程時,如果當前線程總數小于核心線程 corePoolSize
,則新建核心線程,如果超過corePoolSize,則新建非核心線程。
核心線程默認是一直在存活在線程池中,即使處于閑置狀態。
如果指定 ThreadPoolExecutor
的 allowCoreThreadTimeOut
這個屬性為 true,那么核心線程如果處于閑置狀態的話,超過一定時間(keepAliveTime),就會被銷毀掉。
- 2.線程總數 maximumPoolSize
線程中的最大線程數,等于核心線程加上非核心線程。
- 3.超時時間 keepAliveTime
非核心線程超時時長,一個非核心線程的閑置時間超過這個線程設置的時長,就會被銷毀。同時如果設置allowCoreThreadTimeOut = true
,則會作用于核心線程。
- 4 . 時間單位
keepAliveTime
的時間單位 TimeUnit ,枚舉值,有以下幾種:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小時
DAYS : 天
- 5 . 隊列 BlockingQueue
它是一個接口,代表一個任務隊列,維護著等待執行的runnable
, 當所有的核心線程創建之后,就會將待執行的任務存入任務隊列,如果任務隊列滿了,則創建非核心線程,根據具體的實現,有不同的使用情形,下面是常用的任務隊列實現。
SynchronousQueue
任務隊列接受任務時,把任務直接提交給線程處理,如果所有線程都在工作,那就新創建一個線程來處理,所以此種任務隊列一般需要設置maximumPoolSize
為 Integer.MAX_VALUE
,以防出現不能新建線程產生錯誤。
LinkedBlockingQueue
任務隊列接受任務時候,如果當前線程數小于核心線程數,則新建核心線程處理任務,如果當前線程數等于核心線程數,則進入隊列等待,由于這個任務隊列沒有最大值限制,則所有超過核心線程都會被添加到隊列中,這也就導致了maximumPoolSize
的設定失效,線程總數不會超過corePoolSize
ArrayBlockingQueue
限定任務隊列的長度,接受到任務時候,如果沒有達到corePoolSize
的值,則新建核心線程執行任務,如果已經達到了,則入隊列等待,如果隊列已經滿了,則新建非核心線程執行任務,如果線程總數達到了maximumPoolSize
,則發生錯誤
DelayQueue
隊列內元素必須實現Delayed
接口,這就表示你傳進去的任務必須實現Delayed
接口,入隊列的元素,首先會先入隊列,只有達到了指定的延時時間,才會執行任務
- 6 . ThreadFactory
創建線程的方式,Executors
有默認的ThreadFactory
創建方式,一般沒有特別需求可以直接使用它的默認實現方式,提交的任務通過它創建線程
/**
* The default thread factory.
*/
private static class DefaultThreadFactory implements ThreadFactory {
...
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
- 7 . RejectedExecutionHandler
拋出異常專用,也就是線程池的飽和策略,ThreadPoolExecutor
實現了幾種常用異常
ThreadPoolExecutor.AbortPolicy
:默認的策略,丟棄任務并拋出RejectedExecutionException異常,這種策略需要加try catch
,否則程序會直接退出
ThreadPoolExecutor.DiscardPolicy
:也是丟棄任務,但是不拋出異常,空方法。
ThreadPoolExecutor.DiscardOldestPolicy
:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程)。
ThreadPoolExecutor.CallerRunsPolicy
在調用execute的線程里面執行此策略,會阻塞入口
用戶自定義飽和策略(最常用)
實現RejectedExecutionHandler,并自己定義策略模式
關于線程池詳細源碼分析以及使用過程,可以看這篇文章
常見的四種線程池
Executors默認給我們提供了幾種線程池,常用的有四種,都是通過ThreadPoolExecutor
直接或者間接實現的,調用者可以很方便的使用它
- 1 . CachedThreadPool
創建方法:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
好處:
1.線程無限制
2.有空閑線程則復用空閑線程,若無空閑線程則新建線程
3.一定程序減少頻繁創建/銷毀線程,減少系統開銷
- 2 .FixedThreadPool
創建方法:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
只有核心線程,線程總數是一定的
好處:
1.可控制線程最大并發數(同時執行的線程數)
2.超出的線程會在隊列中等待
- 3 . ScheduledThreadPool
創建方法:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
好處:
1 . 這個線程池支持定時或者周期性任務
- 4 . SingleThreadExecutor
創建方法:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
好處:
1.有且僅有一個工作線程執行任務
2.所有任務按照指定順序執行,即遵循隊列的入隊出隊規則
關于這幾種線程池的原理,可以看這篇文章
Android中線程池的使用
作為一名Android
開發者,在平時我們開發中,使用的原生API或者第三方庫,都會或多或少使用線程池,下面舉出幾個我在平時開發中使用到線程池的地方。
- 1 . EventBus中線程池的使用
熟悉EventBus
源碼的人都會知道,它里面有三個Poster
,不熟悉的看這里,它在EventBus
中起到一個線程切換的作用,同時也提供一個線程去執行異步任務。其中它們的默認使用如下
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
可以看見創建了一個CachedThreadPool
,這個線程池都是非核心線程,可以在一定程度上減輕頻繁創建線程所帶來的線程開銷。
- 2 . Okhttp中線程池的使用
在Okhttp
中有個分發器Dispatcher
,在里面維護了一個executorService
,它的創建如下:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
它是完全自定義的ThreadPoolExecutor
,可以看見它的核心線程為0,非核心線程設置為
Integer.MAX_VALUE
和和EventBus
的線程數一樣
- 3 . AsyncTask中線程池的使用
剛學Android的時候,這個AsyncTask
作為異步任務的API
用得還是比較廣的,它里面封裝了線程池和Handler
,在異步任務和UI線程
中切換非常方便,看下其線程池的使用
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());
}
};
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
}
它的核心線程是Math.max(2, Math.min(CPU_COUNT - 1, 4))
, 也就是最少是兩個,CPU_COUNT
是CPU的數,總線程總數是CPU_COUNT * 2 + 1
,而它的任務隊列是new LinkedBlockingQueue<Runnable>(128)
,這樣它的任務隊列的最大長度是128。這樣就達到了控制線程的并發數,當線程達到最大核心線程數時候,其它任務就會被放到任務隊列中。
還有很多地方也用到了線程池,就不一一舉例了。
參考文檔
1 . 詳解 Java 線程池
2 . Android開發者探索
3 . Android開發者進階
4 . Java線程池(ThreadPoolExecutor)原理分析與使用
5 . Java線程Executor框架詳解與使用