線程池(ThreadPool):一種線程使用模式。
What?不(什)知(麼)所(東)謂(東)。那麼首先我們得了解什麼事線程,線程一般理解爲我們順序執行某一任務的最小執行單元,同一時間只做一件事情。而線程的創建和銷燬在系統裏面是有開銷的,而且還比較大。
我們來想想我們使用線程的方式一般爲new Thread() 這樣做是有很多弊端的,1、創建線程是有開銷的 2、線程的生命週期不可控,容易引起內存泄漏 3、線程使用混亂,最後自己創建了多少線程不清楚,無法有效的維護。而線程池的目的就是幫我們有效的維護我們的線程,同時儘量減少線程的創建、銷燬的開銷。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:核心線程池大小
maximumPoolSize:最大線程池大小
keepAliveTime:線程池最大空閒時間
unit:時間單位
workQueue:線程等待隊列
threadFactory:線程創建工廠
handler:拒絕策略
我們可以用ThreadPoolExecutor來創建線程池,也可以用Executors的靜態實現類創建我們的線程池。
1、Executors.newFixedThreadPool(N)
創建有N條核心線程沒有非核心線程的線程池,核心線程不會被回收。當超過N數量的線程需排隊等待前面線程執行完畢方可執行。
private void startTheadPool(){
//通過Executors.newFixedThreadPool(N)創建的線程有N條固定數量線程的線程池,僅有核心線程且沒有超時策略,線程不會被回收。
//該方法創建的線程池沒有非核心線程 當超過N數量的線程會等待前面線程執行完纔會執行
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++){
final int index = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.e(TAG,"CFixedThreadPool Start Run: 任務 = "+index+",線程 = "+Thread.currentThread().getName());
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
}
};
executorService.execute(runnable);
}
}
執行結果如下:
2019-08-13 15:26:44.674 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 0,線程 = pool-1-thread-1
2019-08-13 15:26:44.675 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 1,線程 = pool-1-thread-2
2019-08-13 15:26:46.676 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 2,線程 = pool-1-thread-1
2019-08-13 15:26:46.676 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 3,線程 = pool-1-thread-2
2019-08-13 15:26:48.677 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 4,線程 = pool-1-thread-1
2019-08-13 15:26:48.677 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 5,線程 = pool-1-thread-2
2019-08-13 15:26:50.679 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 7,線程 = pool-1-thread-2
2019-08-13 15:26:50.679 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 6,線程 = pool-1-thread-1
2019-08-13 15:26:52.680 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 9,線程 = pool-1-thread-2
2019-08-13 15:26:52.680 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任務 = 8,線程 = pool-1-thread-1
可以看到一次執行了2條線程,而2秒之後待前2條線程執行完畢才繼續執行下2條線程。而線程始終爲線程池裏的thread-1和thread-2。
2、Executors.newCachedThreadPool()
該方法創建的線程只有非核心線程,數量爲無限大。沒有核心線程。適合做一些量比較大但耗時較短的操作。
private void startTheadPool(){
//CachedThreadPool是一種線程數量不定的線程池,只有非核心線程,線程數量爲無限大。
//比較適合做一些大量的耗時較少的任務
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++){
final int index = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.e(TAG,"CCachedThreadPool Start Run: 任務 = "+index+",線程 = "+Thread.currentThread().getName());
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
}
};
executorService.execute(runnable);
}
}
執行結果如下:
2019-08-13 15:34:00.398 21796-22119/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 0,線程 = pool-2-thread-1
2019-08-13 15:34:00.401 21796-22120/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 1,線程 = pool-2-thread-2
2019-08-13 15:34:00.403 21796-22121/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 2,線程 = pool-2-thread-3
2019-08-13 15:34:00.405 21796-22122/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 3,線程 = pool-2-thread-4
2019-08-13 15:34:00.405 21796-22123/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 4,線程 = pool-2-thread-5
2019-08-13 15:34:00.407 21796-22125/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 6,線程 = pool-2-thread-7
2019-08-13 15:34:00.408 21796-22126/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 7,線程 = pool-2-thread-8
2019-08-13 15:34:00.409 21796-22127/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 8,線程 = pool-2-thread-9
2019-08-13 15:34:00.413 21796-22128/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 9,線程 = pool-2-thread-10
2019-08-13 15:34:00.418 21796-22124/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任務 = 5,線程 = pool-2-thread-6
我們可以看到基本在同一時刻執行了所有的線程,並且執行的線程均未重複。
3、Executors.newScheduledThreadPool(N)
該方法創建N條核心線程和不限制的非核心線程的線程池。一般來說,該線程池執行的爲帶有一定延遲或週期調度的線程任務。
private void startTheadPool(){
//ScheduledExecutorService(N) 有N條核心線程且有非限制的非核心線程,非核心線程閒置時會被立即回收。
//主要用於執行定時任務和具有固定週期的重複任務
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
for (int i = 0; i < 10; i++){
final int index = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.e(TAG,"CScheduleThreadPool Start Run: 任務 = "+index+",線程 = "+Thread.currentThread().getName());
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
}
};
//延遲2秒執行runnable
executorService.schedule(runnable,2000, TimeUnit.MILLISECONDS);
}
}
執行效果爲線程開始後延遲2秒之後纔開始真正的執行線程裏的內容。除了延遲,其效果與FixedThreadPool執行效果類似。
4、Executors.newSingleThreadExecutor()
創建一個只有唯一線程的線程池。所有任務均順序等待該唯一線程的執行。因爲只有唯一線程,所以我們也不用去考慮線程通過的問題。
private void startTheadPool(){
//SingleTheadExecutor只有一個核心線程,無非核心線程 所有任務都在一個線程裏順序執行 不需要線程同步
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++){
final int index = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.e(TAG,"CSingleThreadExecutor Start Run: 任務 = "+index+",線程 = "+Thread.currentThread().getName());
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
}
};
executorService.execute(runnable);
}
}
因爲只有一個唯一線程,所以我們會每2秒執行一個任務,且線程名只有唯一的一個。看下驗證結果。
2019-08-13 15:44:44.175 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 0,線程 = pool-3-thread-1
2019-08-13 15:44:46.177 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 1,線程 = pool-3-thread-1
2019-08-13 15:44:48.180 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 2,線程 = pool-3-thread-1
2019-08-13 15:44:50.180 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 3,線程 = pool-3-thread-1
2019-08-13 15:44:52.182 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 4,線程 = pool-3-thread-1
2019-08-13 15:44:54.185 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 5,線程 = pool-3-thread-1
2019-08-13 15:44:56.186 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 6,線程 = pool-3-thread-1
2019-08-13 15:44:58.188 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 7,線程 = pool-3-thread-1
2019-08-13 15:45:00.190 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 8,線程 = pool-3-thread-1
2019-08-13 15:45:02.191 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任務 = 9,線程 = pool-3-thread-1
可以看到與我們預判一致,沒隔2秒執行一個任務,執行的線程名爲thread-1
以上4種方式 就是我們線程池的常規使用方案,在實際項目中我們可以根據實際場景來擇優使用。