本文轉自博客Vander丶CSDN博客
前言
,.
,-->-->-->,,,.
,,.
.,,,.
線程
在瞭解線程池之前,先給大家介紹下線程的概念:
先看一個燒水的例子,圖中看電視是主線,用戶想在看電視的過程中去完成燒水這個操作,並且不耽誤看電視,看了這張圖,在去了解接下來的概念會更好的理解主線程與子線程的概念。
線程是什麼?
從底層角度來說:
一個線程就是在進程中的一個單一的順序控制流.
而單個進程可以擁有多個併發執行的任務,每個任務都好像有自己的CPU一樣,而其底層的機制就是切分CPU的時間,也就是CPU將輪流給每個任務分配其佔用時間。
每個任務都覺得自己在一直佔用CPU,而事實上是將CPU時間劃分成片段分配給所有的任務。
在多個CPU的環境下,多線程的運作,可以極大的提供程序的運行速度,這就是線程存在的意義。
那麼在Android中,線程的作用是?
首先,先了解下Android下進程和線程的概念:
這裏引用Gityuan作者在知乎上的回答,關於線程和進程的概念
進程:每個app運行時前首先創建一個進程,該進程是由Zygote fork出來的,用於承載App上運行的各種Activity/Service等組件。
進程對於上層應用來說是完全透明的,這也是google有意爲之,讓App程序都是運行在Android Runtime。大多數情況一個App就運行在一個進程中,除非在AndroidManifest.xml中配置Android:process屬性,或通過native代碼fork進程。
線程:線程對應用來說非常常見,比如每次new Thread().start都會創建一個新的線程。該線程與App所在進程之間資源共享,從Linux角度來說進程與線程除了是否共享資源外,並沒有本質的區別,都是一個task_struct結構體,在CPU看來進程或線程無非就是一段可執行的代碼,CPU採用CFS調度算法,保證每個task都儘可能公平的享有CPU時間片。
上面可能還是比較專業,這裏簡要總結下線程在Android的作用:
(1)在Android中線程分主線程和子線程,主線程也被稱爲UI線程,用來處理各種和界面相關的事情,
例 :界面的加載,Activity的生命週期這些都在主線程的範疇之內。
(2)由於主線程比較特殊,因爲本身主線程在處理界面上,用了大部分的消耗,所以主線程不能再處理過於耗時的操作(IO操作,網絡請求,大量的數據操作),否則就會造成ANR現象(程序卡死)。
什麼是ANR?,這裏百度上有比較全的介紹
而造成這種現象的主要原因有:
Activity響應時間超過5s
Broadcast在處理時間超過10s
Service處理時間超過20s
這大部分的原因是主線程進行過於耗時的操作,因爲Activity,Broadcast,Serivce本身都是通過主線程進行承載的。
(3)此時子線程就橫空出世解決了這類問題,Android建議耗時操作必須放在子線程中運行。
(4)而在Android中可以解決耗時問題的角色除了Thread之外還有AsyncTask,HandlerThread,IntentService,都可以實現此類功能,而他們的本質還是傳統的線程。
爲什麼會有線程池?
從字面上來看,線程池是存放,和管理線程的池子。那麼爲什麼會有線程池呢?
先看一個例子,這裏我用Handler和Thread來模擬網絡請求的操作:
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == TASK_ACTION) {
Log.d("收到消息", "更新UI");
}
return false;
}
});
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
mHandler.sendEmptyMessage(TASK_ACTION);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
上面過程,只是用一個Thread來模擬正常的網絡請求,然後通過Handler來回調給UI線程,通知UI線程來刷新,如果對Handler機制不太瞭解,
一篇不錯的Handler介紹的文章
上面只是單純的一個網絡請求,那麼現在需求來了,這個界面不止一個網絡請求,可能存在大量的網絡請求,這時候就會有問題產生:
(1)當大量的網絡請求產生,就會大量的創建和銷燬線程,因此可能會造成過大的性能開銷。
(2)當大量的線程一起運作的時候,可能會造成資源緊張,上面也介紹過線程底層的機制就是切分CPU的時間,而大量的線程同時存在時可能造成互相搶佔資源的現象發生,從而導致阻塞的現象。
基於以上背景,線程池適當的出現可以很好的解決上述的問題,而上述模擬網絡請求也只是一個簡單的例子,而現實情況下,會有好多種情況和上述相似,比如在數據庫操作大數據,多線程下載,在使用Thread的同時都會出現上述情況。
什麼是線程池?
Android中的線程池的概念來源於Java中的Executor,Executor是一個接口,真正的線程池的實現爲ThreadPoolExecutor,ThreadPoolExecutor提供了一系列參數來配置線程池,通過不同的參數可以創建不同的線程池。
線程池的優點:
線程池的出現,恰恰就是解決上面類似問題的痛點,而線程池的優點有:
(1)複用線程池中的線程,避免因爲線程的創建和銷燬所帶來的性能開銷。
(2)能夠有效的控制線程池的最大併發數,避免大量的線程之間因互相搶佔系統資源而導致的阻塞現象。
(3)能夠對線程進行簡單的管理,並提供定時執行以及指定間隔循環執行等功能。
線程池的構造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
上面代碼是創建一個基本的線程池需要的參數,讓我們通過圖來簡要的描述下:
由上圖可以簡要的描述出創建一個基本的線程池需要的參數,以及各個參數的含義,下面將詳細說明各個參數的具體含義。
CorePoolSize
線程的核心線程數。
默認情況下,核心線程數會在線程中一直存活,即使它們處於閒置狀態。
如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置爲true,那麼核心線程就會存在超時策略,這個時間間隔有keepAliveTime所決定,當等待時間超過keepAliveTime所指定的時長後,核心線程就會被停止。
maximumPoolSize
線程池所能容納的最大線程數。
當活動線程數達到這個數值後,後續的新任務將會被阻塞。
keepAliveTime
非核心線程閒置時的超時時長,超過這個時長,非核心線程就會被回收,當ThreadPoolExector的allowCoreThreadTimeOut屬性設置爲True時,keepAliveTime同樣會作用於核心線程。
unit
用於指定keepAliveTime參數的時間單位,這是一個枚舉,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分鐘)等。
TimeUnit.NANOSECONDS 納秒
TimeUnit.MICROSECONDS 微秒
TimeUnit.MILLISECONDS 毫秒
TimeUnit.SECONDS 秒
TimeUnit.MINUTES 分鐘
TimeUnit.HOURS 小時
TimeUnit.DAYS 天
workQueue
線程池中的任務隊列,通過線程池execute方法提交的Runnable對象會存儲在這個參數中。
這個任務隊列是BlockQueue類型,屬於阻塞隊列,就是當隊列爲空的時候,此時取出任務的操作會被阻塞,等待任務加入隊列中不爲空的時候,才能進行取出操作,而在滿隊列的時候,添加操作同樣被阻塞。
如果有想了解的可以參考下這篇文章:
Java多線程-工具篇-BlockingQueue
threadFactory
線程工廠,爲線程池提供創建新線程的功能。ThreadFactory是一個接口,它只有一個方法,newThread(Runnable r),用來創建線程。
ThreadFactory factory =new ThreadFactory() {
private final AtomicInteger mCount =new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "new Thread #" + mCount.getAndIncrement());
}
};
線程池的源碼解析
打開源碼,先把線程池源碼中除了構造參數,其他的一些基本屬性,先給分析一下.
線程池的生命週期
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
將上述高位運算就是將 0 和1以及其他的數值在二進制下,向左移位29位,缺位用0補齊,實際結果就變成:
# 接受新任務,並且處理隊列任務的狀態
RUNNING = 111 000...000 (29個0)
# 不接受新任務,但是會處理隊列任務的狀態
SHUTDOWN = 000 000...000 (29個0不包括前三位)
# 不接受新任務,並且也不會處理隊列任務的狀態
STOP = 001 000...000 (29個0)
# 所有線程池內線程都將被終止,並且將workCount清零,在這裏狀態下將會運行terminated()方法(終止線程池的方法)
TIDYING = 010 000...000 (29個0)
# terminated()方法以及結束的狀態
TERMINATED = 011 000...000 (29個0)
/**
* 獲取到當前線程池的生命週期的狀態
*/
private static int runStateOf(int c) { return c & ~CAPACITY; }
/**
* 獲取當前線程池的工作線程狀態
*/
private static int workerCountOf(int c) { return c & CAPACITY; }
/**
* 通過或運算拼接線程的生命週期狀態和工作線程的個數
*/
private static int ctlOf(int rs, int wc) { return rs | wc; }
上面的三個函數是獲取當前線程池狀態的方法,這裏簡單介紹下:
(1) ctlOf()有兩個參數,一個是生命週期狀態,一個是當前線程池工作線程.
生命週期的狀態格式:
XXX 0000…0000(29個0)
ctlOf()返回的值就是將工作線程數量轉化成2進制拼接在生命週期的二進制後半段上.
(2) runStateOf()和workerCountOf()方法都是讓生命週期的狀態值與CAPACITY和CAPACITY的反碼進行與運算,簡明的說,就是獲得二進制數的高位(前三位)和低位(後29位).
如果大家比較瞭解位運算可以發現:
------> ...
------> ...
所以在進行與運算的同時,可以分別取出前3位和後29位,來分別代表線程池的生命週期和工作線程數.
其他屬性
/**
* 無法執行任務的通知類
* 在Android中不太常用
*/
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
當線程池無法執行任務,這可能由於任務隊列已滿或者是無法成功執行任務.這個時候ThreadPoolExecution就會調用handler的rejectedExecution方法來通知調用者.
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
默認情況下,rejectedExecution會拋出個RejectedExecutionException異常,來說明爲什麼當前無法執行任務.
ThreadPoolExecution爲RejectedExecutionException提供了幾個可選值:
----------------------------CallerRunsPolicy-------------------
public void rejectedExecution(Runnable r, ThreadPoolExecutor e){
if (!e.isShutdown()) {
r.run();
}
}
-------------------------AbortPolicy(默認值)---------------------
public void rejectedExecution(Runnable r, ThreadPoolExecutor e){
throw new RejectedExecutionException(
"Task " + r.toString() +
" rejected from " +e.toString());
}
-------------------------DiscardPolicy--------------------------
}
----------------------DiscardOldestPolicy-----------------------
public void rejectedExecution(Runnable r, ThreadPoolExecutor e){
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
比較重要的方法
線程池有兩個執行的方法,分別是submit()和execute(),這兩個方法本質的含義是一樣的.
從圖上可以看出的,submit()其實還是需要調用execute()去執行任務,而submit()和execute()本質上的不同是submit()將包裝好的任務進行了返回.
submit()
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
public class FutureTask<V> implements RunnableFuture<V> {
.......
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
execute()
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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
下圖是一張execute()方法的基本流程:
從execute()方法中,能看出addWorker()方法,是創建線程(核心線程,非核心線程)的主要方法,而reject()就是線程創建失敗的一個回調.
reject()
那我們來看一下reject()方法,這裏就是通過上述的Handler將通知發出去.然後針對不同的類型的RejectedExecutionHandler,進行不同的處理,這裏我們上文有介紹.
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
下面我們着重看下創建線程的方法:
addWorker()
參數 :
Runnable firstTask:
爲傳遞進來需要執行的任務,也可以設置爲null(在SHUTDOWN情況下,單純的創建線程來執行任務).
boolean core:
需要創建的線程是否需要是核心線程.
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get();
if (runStateOf(c) != rs)
continue retry;
retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
addWorker()方法的注意事項:
(1)增加一個線程,並且會爲其綁定core或者maximum的線程標誌.
(2)如果成功添加線程來執行當前任務,那麼當前線程池的狀態會被刷新.
(3)在添加第一個任務firstTask的這種情況下,新的工作線程會被創建後立即執行任務.
(4)該方法會在線程池STOP狀態或者符合資格去關閉會返回false.
(5)線程工廠創建線程失敗的時候,同樣也會返回false.
(6)在由於線程創建失敗,線程工廠返回的線程爲null,或者發生異常(通常由於在線程執行的過程中發生了OOM),線程池會進行回滾操作.
addWorker()方法執行的幾個階段
第一階段 :
狀態檢查
在創建線程時,首先檢查線程池狀態,防止線程處於STOP,TIDYING,TERMINATED狀態,如果處於上述狀態直接返回false.
然後對於在SHUTDOWN狀態下,只有當前任務隊列不爲空,並且傳遞的任務參數爲null.這種狀態下可以創建線程來執行剩餘任務,除此之外全部直接返回false.
if (rs >= SHUTDOWN &&! (rs == SHUTDOWN && firstTask == null
&&! workQueue.isEmpty()))
return false;
第二階段 :
判斷當前線程池的能否創建線程以及可以創建之後的數量添加校驗
(1)當前線程的數量是否超過線程池的最大容量,以及根據core參數來判斷是否超過設置的核心線程數,和最大線程數.
(2)通過第一步之後就可以創建線程,這裏需要用到compareAndIncrementWorkerCount()通過原子操作來更新線程池的線程數量變化,如果變化數量失敗,這裏有一個重試機制,這個retry關鍵字就是來完成這個操作.
(3)這裏註明下CAPACITY這個常量就是線程池的線程數量的極限
CAPACITY = 1>>29 -1 =2^29-1
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get();
if (runStateOf(c) != rs)
continue retry;
}
第三階段 :
創建線程
通過上述階段,那麼就可以創建線程了,這裏設置了兩個初始的標誌位,來判斷被創建線程的狀態.
boolean workerStarted = false;
boolean workerAdded = false;
如果最終線程創建並添加成功,則返回true,如果線程最終沒有被運行,則調用addWorkerFailed()方法.
由於邏輯並不複雜,這裏就不貼代碼了.
其他相關方法
addWorkedFailed()
在addWorker()方法中,如果線程創建之後,沒有最終運行(workerStarted=false)這時候會調用addWorkedFailed()方法.
/**
* 回滾工作線程的創建操作:
* 1.如果線程的包裝類Worker存在,就將其remove掉.
* 2.remove掉添加線程失敗的Worker,需要刷新當前工作線程的數量
* 3.嘗試終止操作,並且終止這個線程的操作.
*/
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
tryTerminate()
而在addWorkedFailed()方法中,我們發現除了回滾操作,它還調用了tryTerminate()方法,嘗試着去停止線程池.因爲線程池創建線程失敗一般由於異常引起(或OOM),所以這時候需要讓線程池進行停止操作.
注意事項:
如果發生以下兩種情況,使用該方法將會將線程池轉換爲終止狀態(TERMINATED):
1.SHUTDOWN狀態下,隊列爲空的情況下.
2.STOP狀態下.
如果符合上述條件,可以轉換終止狀態時,這時會中斷當前線程池內空閒的線程,以確保終止的信號的傳遞.
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
interruptIdleWorkers()
而在tryTerminate()方法中,這裏中斷線程的操作就是由interruptIdleWorkers()方法進行的.
這個方法作用很明確,就是設置線程中斷操作的方法,唯一注意的地方就是參數onlyOne:
如果爲true,只中斷工作線程中的一個線程.
如果爲false,中斷所有的工作線程.
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
shutdown()
而中斷所有空閒的線程方法則是shutdown()方法,它的核心方法還是調用interruptIdleWorkers()方法.
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown();
} finally {
mainLock.unlock();
}
tryTerminate();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
注意事項:
(1)在shutdown()執行時可以讓現有的任務被執行,但是新的任務不在會被處理.
(2)如果已經是SHUTDOWN狀態,那麼繼續調用不會產生任何效果.
(3)這個方法不會等待所有任務都執行完在提交,如果想先讓所有任務先完成請使用awaitTermination()方法.
awaitTermination()
阻塞至直到當前任務完全結束之後,纔會關閉請求,或者超時的發生,也或者線程被中斷 看哪個發生的快.
參數:d
timeout —- 設置超時時間
unit —- 設置超時時間的單位
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
while (!runStateAtLeast(ctl.get(), TERMINATED)) {
if (nanos <= 0L)
return false;
nanos = termination.awaitNanos(nanos);
}
return true;
} finally {
mainLock.unlock();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
線程池的分類
Android中最常見的四類具有不同功能特性的線程池:
1.FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
);
這是一種數量固定的線程池,當線程處於空閒的時候,並不會被回收,除非線程池被關閉.
當所有的線程都處於活動狀態時,新任務都會處於等待狀態,直到有線程空閒出來.
由於FixedThreadPool中只有核心線程並且這些核心線程不會被回收,這意味着它能夠更加快速地響應外界的請求.
通過構造方法可以看出,FixedThreadPool只有核心線程,並且超時時間爲0(即無超時時間),所以不會被回收.
2.CacheThreadPool
public static ExecutorService newCacheThreadPool(){
return new ThreadPoolExecutor(
0,Integer.MAX_VALUE,
60L,TimeUnit.SECONDS,
new SynchronousQueue<Runnable>()
);
}
它是一種線程數量不定的線程池,它只有非核心線程,並且其最大線程數爲Integer.MAX_VALUE(也就相當於線程池的線程數量可以無限大).
當線程池中所有線程都處於活動的狀態時,線程池會創建新的線程來處理新任務,否則就會複用空閒線程來處理.
值得注意的是,這個線程池中儲存任務的隊列是SynchronousQueue隊列,這個隊列可以理解爲無法儲存的隊列,只有在可以取出的情況下,纔會向其內添加任務.
從整個CacheThreadPool的特性來看:
(1)比較適合執行大量的耗時較少的任務.
(2)當整個線程都處於閒置狀態時,線程池中的線程都會超時而被停止,這時候的CacheThreadPool幾乎不佔任何系統資源的.
3.ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSzie) {
return new ScheduledThreadPoolExecutor(corePoolSzie);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
它的核心線程數量是固定的,而非核心線程數是沒有限制的,並且當非核心線程閒置時會被立即回收.
ScheduThreadPool這類線程池主要用於執行定時任務和具有固定週期的重複任務.
而DelayedWorkQueue這個隊列就是包裝過的DelayedQueue,這個類的特點是在存入時會有一個Delay對象一起存入,代表需要過多少時間才能取出,相當於一個延時隊列.
4.SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return Executors.newSingleThreadExecutor();
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
這類線程池內部只有一個核心線程,它確保所有的任務都在同一個線程中按順序執行.
SingleThreadExecutor的意義在於統一外界所有任務到一個線程,這使得這些任務之間不需要處理線程同步的問題.
參考文檔:
1.安卓開發藝術探索
2.ThreadPoolExecutor解析-主要源碼研究
http://blog.csdn.net/wenhuayuzhihui/article/details/51377174
3.理解java線程的中斷(interrupt)
http://blog.csdn.net/canot/article/details/51087772