Java線程池詳解(二)

作者: 一字馬胡
轉(zhuǎn)載標志 【2017-11-03】

更新日志

日期 更新內(nèi)容 備注
2017-11-03 添加轉(zhuǎn)載標志 持續(xù)更新

一、前言

在總結(jié)了線程池的一些原理及實現(xiàn)細節(jié)之后飞苇,產(chǎn)出了一篇文章:Java線程池詳解(一)远豺,后面的(一)是在本文出現(xiàn)之后加上的歌焦,而本文就成了(二)肩祥。因為在寫完第一篇關(guān)于java線程池的文章之后后室,越發(fā)覺得還有太多內(nèi)容需要補充,每次都是修修補補混狠,總覺得還缺點什么岸霹。在第一篇中,我著重描述了java線程池的原理以及它的實現(xiàn)檀蹋,主要的點在于它是如何工作的松申。而本文的內(nèi)容將更為上層,重點在于如何應用java線程池俯逾,算是對第一篇文章的一點補充,這樣對于java線程池的學習和總結(jié)稍微完整一些舅逸。

使用過java線程池的人應該知道桌肴,我們都習慣使用Executors這個工廠類來獲取我們需要的線程池,而這個工廠不僅僅可以產(chǎn)生一種線程池琉历,而是可以產(chǎn)生若干種不同應用場景的線程池坠七,你應當在合適的場景中使用合適的線程池,以保證最好的效率旗笔。下文將主要剖析這個類的一些細節(jié)彪置,為了保證本文的相對獨立性,可能會提及一些在第一篇文章中提過的內(nèi)容蝇恶,這樣閱讀起來相對流暢一些拳魁,體驗更佳。本文依然不會基于java線程池做更多應用方面的描述撮弧,而是從線程池類型這個角度出發(fā)潘懊,試圖探索不同種類的線程池的特點和使用場景,從某種意義上來說贿衍,這樣描述的意義較于實際的例子來說更為有用授舟。

“授人以魚不如授人以漁” !C潮病J褪鳌!

二、Executors工廠類詳解

介于本文的重點在于Executors這個工廠類奢啥,下面首先列出了Executors這個類提供的一些方法署浩。

Executors方法

本文需要對以上12個類做一些區(qū)分,從其特點出發(fā)扫尺,然后分析其應用場景筋栋。

  • public static ExecutorService newFixedThreadPool(int nThreads)

使用這個方法會產(chǎn)生這樣一個線程池:線程池最多會保持nThreads個線程處于活動狀態(tài),如果當前所有任務(wù)都處于活動狀態(tài)正驻,那么新提交的任務(wù)會被添加到任務(wù)阻塞隊列中去弊攘。總結(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), 你可以使用這個方法來傳遞你自己的線程工廠伤靠,線程工廠是用來干嘛的捣域?就是用來生成線程的,你可以使用線程工廠做一些個性化的線程特性定制宴合。

  • public static ExecutorService newWorkStealingPool(int parallelism)

在了解或者使用這個方法之前焕梅,你你該對java的Fork/Join并行框架有一些了解,如果你想要快速了解一下該部分的內(nèi)容卦洽,可以參考這篇文章:Java Fork/Join并行框架贞言。
從名字上我們就知道這個方法生產(chǎn)出來的線程池具有某種“小偷”的行為,在Fork/Join里面阀蒂,線程的工作模式為“盜竊算法”该窗,也就是在自己的任務(wù)隊列消費完了之后不是進入等到狀態(tài),而是會主動去偷竊別的線程的任務(wù)來做蚤霞,其實是沒有一種獎勵機制來鼓勵這些線程去幫助別的線程去消費任務(wù)的酗失,所以可以認為這些線程都是好人,都為了快速完成任務(wù)協(xié)調(diào)作戰(zhàn)昧绣。這種工作方式的重點在于规肴,每個線程都將有一個任務(wù)隊列,線程之間通過“偷竊”的方式互相幫助來完成任務(wù)的消費滞乙。

可以看下這個方法的實現(xiàn):


return new ForkJoinPool(parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);

可以發(fā)現(xiàn)奏纪,這個方法不是使用我們在第一篇文章中分析了ThreadPoolExecutor來生成線程池。而是使用了ForkJoinPool斩启,也就是Fork/Join里面的線程池序调,關(guān)于ForkJoinPool更為深入的分析不再本文的涉及范圍內(nèi),你只要知道Fork/Join框架的一般運行原理就可以了兔簇,下面的描述可以幫助你決策你是否需要該方法提供的線程池來工作:


     * 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)生的線程池僅僅有一個Worker,任何時刻都將只有一個Worker在工作边酒,添加的任務(wù)有很大概率被放在阻塞任務(wù)隊列中等待執(zhí)行经柴。這些任務(wù)會被順序執(zhí)行,這個方法的返回值其實是對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òu)造時傳遞的參數(shù)e來完成,更像是代理氓涣。而e是什么牛哺?看下面的代碼:



ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())

其實就是一個只有一個線程的ThreadPoolExecutor。

  • public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

參考newSingleThreadExecutor()劳吠,多了一個線程工廠參數(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秒,那么這個線程池的工作模式為:只要有任務(wù)唄提交蠢古,而且當前沒有空閑的線程可用奴曙,那么就會創(chuàng)建一個新的Worker來工作,一個線程工作完了之后會緩存(idle)60秒便瑟,如果60秒之內(nèi)有新的任務(wù)提交缆毁,則會被喚醒進入工作模式,否則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ù)量較多,那么使用該類型的線程池可以滿足吠裆,而且該線程池還有自動收縮的功能伐谈,在不需要那么多線程的時候,會自動回收線程试疙,釋放資源诵棵。

  • public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

參考newCachedThreadPool()。

  • public static ScheduledExecutorService newSingleThreadScheduledExecutor()

只有一個線程的調(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。

通過上面的分析忠蝗,我們應該對java線程池的理解更為深入现横,再次說明,本文僅僅是對第一篇java線程池文章內(nèi)容的補充阁最,你應該首先閱讀第一篇文章:Java線程池詳解(一)之后再來閱讀本文戒祠,那樣內(nèi)容上更完整,但是單獨閱讀本文一樣具備獨立性闽撤,但是收獲肯定沒有同時閱讀兩篇文章那樣多得哆。

當然,還有許多需要補充的內(nèi)容哟旗,比如Fork/Join框架的線程池的實現(xiàn)原理以及其細節(jié)贩据,以及線程池使用的阻塞隊列的特點以及實現(xiàn)細節(jié),這些內(nèi)容要更為底層闸餐,需要的知識與要求的能力要多更高饱亮,會在以后的文章中陸續(xù)來探索。

掃碼入群
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舍沙,一起剝皮案震驚了整個濱河市近上,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拂铡,老刑警劉巖壹无,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異感帅,居然都是意外死亡斗锭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門失球,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岖是,“玉大人,你說我怎么就攤上這事实苞〔虺牛” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵黔牵,是天一觀的道長聪轿。 經(jīng)常有香客問我,道長荧止,這世上最難降的妖魔是什么屹电? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任阶剑,我火速辦了婚禮,結(jié)果婚禮上危号,老公的妹妹穿的比我還像新娘牧愁。我一直安慰自己,他們只是感情好外莲,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布猪半。 她就那樣靜靜地躺著,像睡著了一般偷线。 火紅的嫁衣襯著肌膚如雪磨确。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天声邦,我揣著相機與錄音乏奥,去河邊找鬼。 笑死亥曹,一個胖子當著我的面吹牛邓了,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播媳瞪,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼骗炉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛇受?” 一聲冷哼從身側(cè)響起句葵,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兢仰,沒想到半個月后乍丈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡把将,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年诗赌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秸弛。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖洪碳,靈堂內(nèi)的尸體忽然破棺而出递览,到底是詐尸還是另有隱情,我是刑警寧澤瞳腌,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布绞铃,位于F島的核電站,受9級特大地震影響嫂侍,放射性物質(zhì)發(fā)生泄漏儿捧。R本人自食惡果不足惜荚坞,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望菲盾。 院中可真熱鬧颓影,春花似錦、人聲如沸懒鉴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽临谱。三九已至璃俗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悉默,已是汗流浹背城豁。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留抄课,地道東北人唱星。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像剖膳,于是被迫代替她去往敵國和親魏颓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 序言 近日后臺需要一些數(shù)據(jù)吱晒,需要從網(wǎng)上爬取甸饱,但是爬取的過程中,由于訪問速度太頻繁仑濒,造成IP被封叹话,最終通過線程池解決...
    HusterYP閱讀 838評論 0 3
  • 一.Java中的ThreadPoolExecutor類 java.uitl.concurrent.ThreadPo...
    誰在烽煙彼岸閱讀 643評論 0 0
  • 清明節(jié)快到了! 清明節(jié)前一天墩瞳,是上墳的日子驼壶。這是我們那兒的叫法,有的稱燒紙喉酌,有的叫祭祖热凹,有的叫掃墓。小時候泪电,沒有賣...
    雪山牧場閱讀 515評論 2 14
  • 遇到一堆關(guān)于小麥的單詞般妙,所以把小麥的品種簡單學習了一下: wheat barley rye spelt kamut...
    馬文Marvin閱讀 441評論 0 0
  • 親愛的朋友, 祝好相速!今天已是一月二十九日碟渺,還有兩天便是新的一月了,不知你這個月過得如何突诬? 今天讀完了阿瑟克拉克...
    居無所處閱讀 264評論 0 1