線程池有啥好處
- 降低資源消耗:通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
- 提高響應(yīng)速度:當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
- 提高線程的可管理性:可以有效的控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過多資源競爭,避免堵塞。提供定時(shí)執(zhí)行,定期執(zhí)行,單線程,并發(fā)控制等功能。
介紹一下ThreadPoolExecutor
構(gòu)造器與參數(shù)
在java源碼中它有四個(gè)構(gòu)造器,分別如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
最終都是調(diào)用最下面那個(gè)構(gòu)造方法,只是參數(shù)不同罷了。那現(xiàn)在來說說這些參數(shù)都是什么意思。
- corePoolSize:線程池中所保存的核心線程數(shù),包括空閑線程。
- maximumPoolSize:線程池允許創(chuàng)建的最大線程數(shù)。
- keepAliveTime:線程池的工作線程空閑時(shí)間。線程池的工作線程空閑后,保持存活的時(shí)間。所以如果任務(wù)很多,并且每個(gè)任務(wù)執(zhí)行的時(shí)間比較短,可以調(diào)大這個(gè)時(shí)間,提高線程的利用率。
- TimeUnit:線程活動(dòng)保持時(shí)間的單位。可選的單位有天,小時(shí),分鐘等。
-
BlockingQueue:任務(wù)執(zhí)行前保存任務(wù)的隊(duì)列,僅保存由execute方法提交的Runnale任務(wù)。
隊(duì)列的種類:
- ArrayBlockingQueue: 是一個(gè)基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列,此隊(duì)列按FIFO(先進(jìn)先出)原則對(duì)元素進(jìn)行排序。
- LinkedBlockingQueue:一個(gè)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,此隊(duì)列按FIFO原則對(duì)元素進(jìn)行排序,吞吐量通常高于ArrayBlockingQueue。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個(gè)隊(duì)列。
- SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。每插入操作必須等到另一下線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue,靜態(tài)工廠方法Executors.newCachedThreadPool使用了這個(gè)隊(duì)列。
- PriorityBlockingQueue:一個(gè)具有優(yōu)先級(jí)的無限阻塞隊(duì)列。
它與corePoolSize和maximumPoolSize的關(guān)系如下:
- ThreadFactory :用于設(shè)置創(chuàng)建線程的工廠,可以通過線程工廠給每個(gè)創(chuàng)建出來的線程設(shè)置更有意義的名字和優(yōu)先級(jí)等。
- RejectedExecutionHandler :飽和策略。當(dāng)線程池處于飽和狀態(tài),那么必須采取一種策略處理提交的新任務(wù)。這個(gè)策略默認(rèn)情況下是AbortPolicy,表示無法處理新任務(wù)時(shí)拋出異常。以下是JDK1.5提供的四種策略。
- AbortPolicy:直接拋出異常。
- CallerRunsPolicy:只用調(diào)用者所在線程來運(yùn)行任務(wù)
- DiscardOldestPolicy 丟棄隊(duì)列里最近的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)。
- DiscardPolicy 不處理,丟棄掉
當(dāng)然也可以根據(jù)應(yīng)用場景需要來實(shí)現(xiàn)RejectedExecutionHandler接口自定義策略,如記錄日志或持久化不能處理的任務(wù)。
2 執(zhí)行方法
- execute方法
我們可以使用execute提交任務(wù),但是execute沒有返回值,所以無法判斷任務(wù)是否被線程池執(zhí)行成功。
threadsPool.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
- submit 方法
我們也可以使用submit方法來提交任務(wù),它會(huì)返回一個(gè)future,那么我們可以通過這個(gè)future來判斷任務(wù)是否執(zhí)行成功,通過future的get方法來獲取返回值,get方法會(huì)阻塞住直到任務(wù)完成,而使用get(long timeout, TimeUnit unit)方法則會(huì)阻塞一段時(shí)間后立即返回,這時(shí)有可能任務(wù)沒有執(zhí)行完。
Future<Object> future = executor.submit(task);
try {
Object s = future.get();
} catch (InterruptedException e) {
// 處理中斷異常
} catch (ExecutionException e) {
// 處理無法執(zhí)行任務(wù)異常
} finally {
// 關(guān)閉線程池
executor.shutdown();
}
- shutdown與shutdownNow
通過調(diào)用shutdown或shutdownNow方法來關(guān)閉線程,它們的原理是遍歷線程池中的工作線程,然后逐個(gè)調(diào)用線程的interrupt方法來中斷線程,所以無法響應(yīng)中斷的任務(wù)可能永遠(yuǎn)無法終止。但它們存在一定的區(qū)別,shutdownNow首先將線程池的狀態(tài)設(shè)置成stop,,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程,并返回等待執(zhí)行任務(wù)的列表,而shutdown只是將線程池的狀態(tài)設(shè)置成SHUTDOWN狀態(tài),然后中斷所有沒有正在執(zhí)行任務(wù)的線程。
Executors與ThreadPoolExecutor
Executors提供四種線程池
- newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
創(chuàng)建一個(gè)可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。線程池為無限大,當(dāng)執(zhí)行第二個(gè)任務(wù)時(shí)第一個(gè)任務(wù)已經(jīng)完成,會(huì)復(fù)用執(zhí)行第一個(gè)任務(wù)的線程,而不用每次新建線程。
- **newFixedThreadPool **
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
創(chuàng)建一個(gè)定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。定長線程池的大小最好根據(jù)系統(tǒng)資源進(jìn)行設(shè)置,如Runtime.getRuntime().availableProcessors()。
- **newScheduledThreadPool **
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
ScheduledThreadPoolExecutor是ThreadPoolExecutor的子類。
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
其中的隊(duì)列是DelayedWorkQueue,支持定時(shí)及周期性任務(wù)執(zhí)行.
比如說:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// 表示延遲3秒執(zhí)行
scheduledThreadPool.schedule(new Runnable() {
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
}
// 表示延遲1秒后每3秒執(zhí)行一次
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
}
- **newSingleThreadExecutor **
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來執(zhí)行任務(wù),保證所有的任務(wù)按指定順序執(zhí)行。
查看Executors源碼我們知道,Executors類提供了使用ThreadPoolExecutor 的簡單的 ExecutorService 實(shí)現(xiàn),也就是上面所說的四種Executors線程池,但是 ThreadPoolExecutor 提供的功能遠(yuǎn)不止于此。 不過在Java doc中,并不提倡我們直接使用ThreadPoolExecutor,而是使用Executors類中提供的幾個(gè)靜態(tài)方法來創(chuàng)建線程池。
我們可以在創(chuàng)建 ThreadPoolExecutor 實(shí)例時(shí)指定活動(dòng)線程的數(shù)量,我們也可以限制線程池的大小并且創(chuàng)建我們自己的 RejectedExecutionHandler 實(shí)現(xiàn)來處理不能適應(yīng)工作隊(duì)列的工作。
合理配置線程池大小
遵循兩原則:
1、如果是CPU密集型任務(wù),就需要盡量壓榨CPU,參考值可以設(shè)為 NCPU+1
2、如果是IO密集型任務(wù),參考值可以設(shè)置為2*NCPU
當(dāng)然,這只是一個(gè)參考值,具體的設(shè)置還需要根據(jù)實(shí)際情況進(jìn)行調(diào)整,比如可以先將線程池大小設(shè)置為參考值,再觀察任務(wù)運(yùn)行情況和系統(tǒng)負(fù)載、資源利用率來進(jìn)行適當(dāng)調(diào)整。