一滔灶、前言
在總結(jié)了線程池的一些原理及實(shí)現(xiàn)細(xì)節(jié)之后,產(chǎn)出了一篇文章:Java線程池詳解(一)吼肥,后面的(一)是在本文出現(xiàn)之后加上的录平,而本文就成了(二)。因?yàn)樵趯懲甑谝黄P(guān)于java線程池的文章之后缀皱,越發(fā)覺得還有太多內(nèi)容需要補(bǔ)充斗这,每次都是修修補(bǔ)補(bǔ),總覺得還缺點(diǎn)什么唆鸡。在第一篇中涝影,我著重描述了java線程池的原理以及它的實(shí)現(xiàn),主要的點(diǎn)在于它是如何工作的争占。而本文的內(nèi)容將更為上層,重點(diǎn)在于如何應(yīng)用java線程池序目,算是對第一篇文章的一點(diǎn)補(bǔ)充臂痕,這樣對于java線程池的學(xué)習(xí)和總結(jié)稍微完整一些。
使用過java線程池的人應(yīng)該知道猿涨,我們都習(xí)慣使用Executors這個(gè)工廠類來獲取我們需要的線程池握童,而這個(gè)工廠不僅僅可以產(chǎn)生一種線程池,而是可以產(chǎn)生若干種不同應(yīng)用場景的線程池叛赚,你應(yīng)當(dāng)在合適的場景中使用合適的線程池澡绩,以保證最好的效率稽揭。下文將主要剖析這個(gè)類的一些細(xì)節(jié),為了保證本文的相對獨(dú)立性肥卡,可能會提及一些在第一篇文章中提過的內(nèi)容溪掀,這樣閱讀起來相對流暢一些,體驗(yàn)更佳步鉴。本文依然不會基于java線程池做更多應(yīng)用方面的描述揪胃,而是從線程池類型這個(gè)角度出發(fā),試圖探索不同種類的線程池的特點(diǎn)和使用場景氛琢,從某種意義上來說喊递,這樣描述的意義較于實(shí)際的例子來說更為有用。
“授人以魚不如授人以漁” Q羲啤IЭ薄!撮奏!
二俏讹、Executors工廠類詳解
介于本文的重點(diǎn)在于Executors這個(gè)工廠類,下面首先列出了Executors這個(gè)類提供的一些方法挽荡。
本文需要對以上12個(gè)類做一些區(qū)分藐石,從其特點(diǎn)出發(fā),然后分析其應(yīng)用場景定拟。
- public static ExecutorService newFixedThreadPool(int nThreads)
使用這個(gè)方法會產(chǎn)生這樣一個(gè)線程池:線程池最多會保持nThreads個(gè)線程處于活動(dòng)狀態(tài)于微,如果當(dāng)前所有任務(wù)都處于活動(dòng)狀態(tài),那么新提交的任務(wù)會被添加到任務(wù)阻塞隊(duì)列中去青自≈暌溃總結(jié)一下就是:使用固定大小的線程池,并發(fā)數(shù)是固定的延窜。
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
- public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
相比于newFixedThreadPool(int nThreads)恋腕, 你可以使用這個(gè)方法來傳遞你自己的線程工廠,線程工廠是用來干嘛的逆瑞?就是用來生成線程的荠藤,你可以使用線程工廠做一些個(gè)性化的線程特性定制。
- public static ExecutorService newWorkStealingPool(int parallelism)
在了解或者使用這個(gè)方法之前获高,你你該對java的Fork/Join并行框架有一些了解哈肖,如果你想要快速了解一下該部分的內(nèi)容葵腹,可以參考這篇文章:Java Fork/Join并行框架磷醋。
從名字上我們就知道這個(gè)方法生產(chǎn)出來的線程池具有某種“小偷”的行為,在Fork/Join里面工猜,線程的工作模式為“盜竊算法”,也就是在自己的任務(wù)隊(duì)列消費(fèi)完了之后不是進(jìn)入等到狀態(tài)币狠,而是會主動(dòng)去偷竊別的線程的任務(wù)來做游两,其實(shí)是沒有一種獎(jiǎng)勵(lì)機(jī)制來鼓勵(lì)這些線程去幫助別的線程去消費(fèi)任務(wù)的,所以可以認(rèn)為這些線程都是好人漩绵,都為了快速完成任務(wù)協(xié)調(diào)作戰(zhàn)贱案。這種工作方式的重點(diǎn)在于,每個(gè)線程都將有一個(gè)任務(wù)隊(duì)列渐行,線程之間通過“偷竊”的方式互相幫助來完成任務(wù)的消費(fèi)轰坊。
return new ForkJoinPool(parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
可以發(fā)現(xiàn),這個(gè)方法不是使用我們在第一篇文章中分析了ThreadPoolExecutor來生成線程池祟印。而是使用了ForkJoinPool肴沫,也就是Fork/Join里面的線程池,關(guān)于ForkJoinPool更為深入的分析不再本文的涉及范圍內(nèi)蕴忆,你只要知道Fork/Join框架的一般運(yùn)行原理就可以了颤芬,下面的描述可以幫助你決策你是否需要該方法提供的線程池來工作:
* Creates a thread pool that maintains enough threads to support
* the given parallelism level, and may use multiple queues to
* reduce contention. The parallelism level corresponds to the
* maximum number of threads actively engaged in, or available to
* engage in, task processing. The actual number of threads may
* grow and shrink dynamically. A work-stealing pool makes no
* guarantees about the order in which submitted tasks are
* executed.
- public static ExecutorService newWorkStealingPool()
參考newWorkStealingPool(int parallelism)。
- public static ExecutorService newSingleThreadExecutor()
下面是對該方法的描述:
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
可以從方法的名字上知道套鹅,該方法產(chǎn)生的線程池僅僅有一個(gè)Worker站蝠,任何時(shí)刻都將只有一個(gè)Worker在工作,添加的任務(wù)有很大概率被放在阻塞任務(wù)隊(duì)列中等待執(zhí)行卓鹿。這些任務(wù)會被順序執(zhí)行菱魔,這個(gè)方法的返回值其實(shí)是對ThreadPoolExecutor的一層包裝,下面的代碼展示了最終執(zhí)行任務(wù)的類:
static class DelegatedExecutorService extends AbstractExecutorService {
private final ExecutorService e;
DelegatedExecutorService(ExecutorService executor) { e = executor; }
public void execute(Runnable command) { e.execute(command); }
public void shutdown() { e.shutdown(); }
public List<Runnable> shutdownNow() { return e.shutdownNow(); }
public boolean isShutdown() { return e.isShutdown(); }
public boolean isTerminated() { return e.isTerminated(); }
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return e.awaitTermination(timeout, unit);
}
public Future<?> submit(Runnable task) {
return e.submit(task);
}
public <T> Future<T> submit(Callable<T> task) {
return e.submit(task);
}
public <T> Future<T> submit(Runnable task, T result) {
return e.submit(task, result);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
return e.invokeAll(tasks);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
return e.invokeAll(tasks, timeout, unit);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
return e.invokeAny(tasks);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return e.invokeAny(tasks, timeout, unit);
}
}
從上面的代碼可以看出吟孙,這個(gè)類其實(shí)就是使用了構(gòu)造時(shí)傳遞的參數(shù)e來完成澜倦,更像是代理。而e是什么杰妓?看下面的代碼:
ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
其實(shí)就是一個(gè)只有一個(gè)線程的ThreadPoolExecutor藻治。
- public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
參考newSingleThreadExecutor(),多了一個(gè)線程工廠參數(shù)巷挥。
- public static ExecutorService newCachedThreadPool()
首先看它的方法體內(nèi)容:
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
可以看到桩卵,核心線程數(shù)量為0,而上限為Integer.MAX_VALUE倍宾,而且keepAliveTime為60秒雏节,那么這個(gè)線程池的工作模式為:只要有任務(wù)唄提交,而且當(dāng)前沒有空閑的線程可用高职,那么就會創(chuàng)建一個(gè)新的Worker來工作矾屯,一個(gè)線程工作完了之后會緩存(idle)60秒,如果60秒之內(nèi)有新的任務(wù)提交初厚,則會被喚醒進(jìn)入工作模式,否則60秒后就會被回收〔蹋可以參考下面的描述:
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
從描述上排作,我們可以想到,其實(shí)這種類型的線程池比較適合于短期高流量的場景亚情,也就是我們所說的“秒殺”場景妄痪,在那樣的場景下,需要的線程數(shù)量較多楞件,那么使用該類型的線程池可以滿足衫生,而且該線程池還有自動(dòng)收縮的功能,在不需要那么多線程的時(shí)候土浸,會自動(dòng)回收線程罪针,釋放資源。
- public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
參考newCachedThreadPool()黄伊。
- public static ScheduledExecutorService newSingleThreadScheduledExecutor()
只有一個(gè)線程的調(diào)度線程池泪酱,類似于newSingleThreadExecutor,但是該方法生產(chǎn)的線程池具備調(diào)度功能还最,下面是對該方法的描述:
* Creates a single-threaded executor that can schedule commands
* to run after a given delay, or to execute periodically.
* (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newScheduledThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
- public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
參考newSingleThreadExecutor和newSingleThreadScheduledExecutor墓阀。
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
參考newFixedThreadPool,但是返回類型不一樣拓轻。
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
參考newFixedThreadPool斯撮。
通過上面的分析,我們應(yīng)該對java線程池的理解更為深入扶叉,再次說明勿锅,本文僅僅是對第一篇java線程池文章內(nèi)容的補(bǔ)充,你應(yīng)該首先閱讀第一篇文章:Java線程池詳解(一)之后再來閱讀本文辜梳,那樣內(nèi)容上更完整粱甫,但是單獨(dú)閱讀本文一樣具備獨(dú)立性,但是收獲肯定沒有同時(shí)閱讀兩篇文章那樣多作瞄。
當(dāng)然茶宵,還有許多需要補(bǔ)充的內(nèi)容,比如Fork/Join框架的線程池的實(shí)現(xiàn)原理以及其細(xì)節(jié)宗挥,以及線程池使用的阻塞隊(duì)列的特點(diǎn)以及實(shí)現(xiàn)細(xì)節(jié)乌庶,這些內(nèi)容要更為底層,需要的知識與要求的能力要多更高契耿,會在以后的文章中陸續(xù)來探索瞒大。