Java線程池基礎(chǔ)知識

五一疯、線程池

在我們的開發(fā)中經(jīng)常會使用到多線程撼玄。例如在Android中,由于主線程的諸多限制墩邀,像網(wǎng)絡(luò)請求等一些耗時(shí)的操作我們必須在子線程中運(yùn)行掌猛。我們往往會通過new Thread來開啟一個(gè)子線程,待子線程操作完成以后通過Handler切換到主線程中運(yùn)行眉睹。這么以來我們無法管理我們所創(chuàng)建的子線程荔茬,并且無限制的創(chuàng)建子線程,它們相互之間競爭辣往,很有可能由于占用過多資源而導(dǎo)致死機(jī)或者OOM兔院。所以在Java中為我們提供了線程池來管理我們所創(chuàng)建的線程。

為什么要使用多線程站削?

  1. 更多的處理器核心

    線程是大多數(shù)操作系統(tǒng)調(diào)度的基本單元坊萝,一個(gè)程序作為一個(gè)進(jìn)程來運(yùn)行,程序運(yùn)行過程中能夠創(chuàng)建多個(gè)線程许起,而一個(gè)線程在一個(gè)時(shí)刻只能運(yùn)行在一個(gè)處理器核心上十偶,采用多線程技術(shù),將計(jì)算邏輯分配到多個(gè)處理器核心上园细,就會顯示減少程序的處理時(shí)間惦积,而現(xiàn)在計(jì)算機(jī)都是多核處理器,隨著核心的增加而變得更加有效率

  2. 更快的響應(yīng)時(shí)間

    在編寫一些較為復(fù)雜的代碼(指的是復(fù)雜的邏輯)猛频,例如狮崩,一筆訂單的創(chuàng)建,包括訂單的數(shù)據(jù)插入鹿寻、生成訂單快照睦柴、通知賣家和處理貨品銷售數(shù)量等,我們可以使用多線程技術(shù)毡熏,將一些操作性不強(qiáng)的操作(生成訂單坦敌、通知賣家)派發(fā)給其他線程來處理。這樣的好處是響應(yīng)用戶請求的線程能夠盡快地處理完成痢法,縮短響應(yīng)時(shí)間狱窘,提升用戶體驗(yàn)

  3. 更好的編程模型

    Java為多線程編程提供了良好、考究并且一致的編程模型财搁,使開發(fā)人員能夠更加專注于問題的解決蘸炸,為遇到的問題建立合適的模型,而不是考慮如何將其多線程化

線程池的優(yōu)勢

  • 重用線程池中的線程妇拯,避免因?yàn)榫€程創(chuàng)建和銷毀所帶的性能開銷

  • 能夠有效控制線程池的最大并發(fā)數(shù)幻馁,避免大量的線程之間因?yàn)榛ハ鄵屨枷到y(tǒng)資源而導(dǎo)致的阻塞現(xiàn)象

  • 能夠?qū)€程進(jìn)行簡單的管理洗鸵,并提供定時(shí)執(zhí)行以及指定間隔循環(huán)執(zhí)行功能

線程池的使用

  1. 使用步驟

    // 創(chuàng)建線程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
                                                 MAXIMUM_POOL_SIZE,
                                                 KEEP_ALIVE,
                                                 TimeUnit.SECONDS,
                                                 sPoolWorkQueue,
                                                 sThreadFactory);
    // 向線程池提交任務(wù)
    threadPool.execute(new Runnable() {
        @Override
        public void run() {
            ... // 線程執(zhí)行的任務(wù)
        }
    });
    // 關(guān)閉線程池
    threadPool.shutdown(); // 設(shè)置線程池的狀態(tài)為SHUTDOWN越锈,然后中斷所有沒有正在執(zhí)行任務(wù)的線程
    threadPool.shutdownNow(); // 設(shè)置線程池的狀態(tài)為 STOP仗嗦,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程,并返回等待執(zhí)行任務(wù)的列表
    
  2. 七大參數(shù)

    • corePoolSize

      線程池中的核心線程數(shù)甘凭,默認(rèn)情況下稀拐,核心線程一直存活在線程池中,即便他們在線程池中處于閑置狀態(tài)丹弱。除非我們將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)為true的時(shí)候德撬,這時(shí)候處于閑置的核心線程在等待新任務(wù)到來時(shí)會有超時(shí)策略,這個(gè)超時(shí)時(shí)間由keepAliveTime來指定躲胳。一旦超過所設(shè)置的超時(shí)時(shí)間蜓洪,閑置的核心線程就會被終止。

    • maximumPoolSize

      線程池中所容納的最大線程數(shù)坯苹,如果活動的線程達(dá)到這個(gè)數(shù)值以后隆檀,后續(xù)的新任務(wù)將會被阻塞。包含核心線程數(shù)+非核心線程數(shù)粹湃。

    • keepAliveTime

      非核心線程閑置時(shí)的超時(shí)時(shí)長恐仑,對于非核心線程,閑置時(shí)間超過這個(gè)時(shí)間为鳄,非核心線程就會被回收裳仆。只有對ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)為true的時(shí)候,這個(gè)超時(shí)時(shí)間才會對核心線程產(chǎn)生效果孤钦。

    • TimeUnit

      用于指定keepAliveTime參數(shù)的時(shí)間單位歧斟。他是一個(gè)枚舉,可以使用的單位有天(TimeUnit.DAYS)偏形,小時(shí)(TimeUnit.HOURS)静袖,分鐘(TimeUnit.MINUTES),毫秒(TimeUnit.MILLISECONDS)壳猜,微秒(TimeUnit.MICROSECONDS, 千分之一毫秒)和毫微秒(TimeUnit.NANOSECONDS, 千分之一微秒)勾徽;

    • workQueue

      線程池中保存等待執(zhí)行的任務(wù)的阻塞隊(duì)列。通過線程池中的execute方法提交的Runable對象都會存儲在該隊(duì)列中统扳。

    • ThreadFactory

      線程工廠喘帚,為線程池提供新線程的創(chuàng)建。ThreadFactory是一個(gè)接口咒钟,里面只有一個(gè)newThread方法吹由。 默認(rèn)為DefaultThreadFactory類

    • RejectedExecutionHandler

      飽和策略

  3. 執(zhí)行流程

    https://note.youdao.com/yws/public/resource/7ad083d3cb19a5c9a95bc4a3445ca9d7/xmlnote/2436B2BC3E464E97A1A3319A9087D553/4783
    • 提交任務(wù)后,如果線程池的線程數(shù)未達(dá)到核心線程數(shù)朱嘴,則創(chuàng)建核心線程處理任務(wù)

    • 如果線程數(shù)大于或等于核心線程數(shù)倾鲫,則將任務(wù)加入到任務(wù)隊(duì)列中粗合,線程池中的空閑線程會不斷地從任務(wù)隊(duì)列中取出任務(wù)進(jìn)行處理。

    • 如果任務(wù)隊(duì)列已經(jīng)滿了乌昔,并且線程數(shù)沒有達(dá)到最大的線程數(shù)隙疚,則會創(chuàng)建非核心線程去處理任務(wù)

    • 如果線程數(shù)超過了最大的線程數(shù),則執(zhí)行飽和策略磕道。

  4. 常用的線程池

    1. 定長線程池(FixedThreadPool)

      它是一種線程數(shù)量固定的線程池供屉,當(dāng)線程處于空閑狀態(tài)時(shí),它們并不會被回收溺蕉,除非線程池關(guān)閉了伶丐。當(dāng)所有線程都處于活動狀態(tài)時(shí),新任務(wù)都會處于等待狀態(tài)疯特,直到有線程空閑出來哗魂。由于FixedThreadPool只有核心線程并且這些核心線程不會被回收,這意味能夠更加快速的響應(yīng)外界的請求漓雅。核心線程沒有超時(shí)機(jī)制录别,另外任務(wù)隊(duì)列沒有大小限制(LinkedBlockingQueue<Runnable>)。

      特點(diǎn):只有核心線程故硅,線程數(shù)量固定庶灿,任務(wù)隊(duì)列為鏈表結(jié)構(gòu)的有界隊(duì)列。

      應(yīng)用場景:控制線程最大并發(fā)數(shù)

    2. 定時(shí)線程池(ScheduledThreadPool )

      主要用于執(zhí)行定時(shí)任務(wù)和具有固定周期的重復(fù)任務(wù)吃衅。核心線程數(shù)固定往踢,非核心線程數(shù)沒有限制,并且當(dāng)非核心線程閑置時(shí)會立即被回收徘层。任務(wù)隊(duì)列為延時(shí)阻塞隊(duì)列(DelayedWorkQueue<RunnableScheduledFuture>),Future為任務(wù)的包裝類

      特點(diǎn):核心線程數(shù)量固定峻呕,非核心線程數(shù)量無限,執(zhí)行完閑置10ms后回收趣效,任務(wù)隊(duì)列為延時(shí)阻塞隊(duì)列瘦癌。

      應(yīng)用場景:執(zhí)行定時(shí)或周期性的任務(wù)。

    3. 可緩存線程池(CachedThreadPool)

      它是一個(gè)線程數(shù)量不定的線程池跷敬,只有非核心線程讯私,并且最大線程數(shù)為Integer.MAX_VALUE,實(shí)際上就相當(dāng)于最大線程數(shù)量可以任意大西傀。當(dāng)線程中的線程都處于活躍狀態(tài)時(shí)斤寇,線程池就會創(chuàng)建新的線程來處理任務(wù),否則就會利用空閑的線程來處理新任務(wù)拥褂。線程池中的線程都有超時(shí)機(jī)制娘锁,60秒,超過閑置時(shí)間就會被回收饺鹃。使用的任務(wù)隊(duì)列為不存儲元素的SynchronousQueue<Runnable>莫秆,相當(dāng)于一個(gè)空集合间雀,這就導(dǎo)致任何線程都會立刻被執(zhí)行。當(dāng)整個(gè)線程池都屬于空閑狀態(tài)時(shí)镊屎,線程池中的線程都會超時(shí)而停止惹挟,這個(gè)時(shí)候池中相當(dāng)于是沒有線程的,幾乎不會占有任何系統(tǒng)資源的杯道。

      特點(diǎn):無核心線程匪煌,非核心線程數(shù)量無限责蝠,執(zhí)行完閑置60s后回收党巾,任務(wù)隊(duì)列為不存儲元素的阻塞隊(duì)列。

      應(yīng)用場景:執(zhí)行大量霜医、耗時(shí)少的任務(wù)齿拂。

    4. 單線程化線程池(SingleThreadExecutor)

      內(nèi)部只有一個(gè)核心線程,它確保所有任務(wù)都在同一個(gè)線程中按順序執(zhí)行肴敛,SingleThreadExecutor存在的意義在于統(tǒng)一所有的外界任務(wù)到一個(gè)線程中署海,使得這些任務(wù)不需要處理線程同步問題。任務(wù)隊(duì)列為LinkedBlockingQueue<Runnable>

      特點(diǎn):只有1個(gè)核心線程医男,無非核心線程砸狞,執(zhí)行完立即回收,任務(wù)隊(duì)列為鏈表結(jié)構(gòu)的有界隊(duì)列镀梭。

      應(yīng)用場景:不適合并發(fā)但可能引起IO阻塞性及影響UI線程響應(yīng)的操作刀森,如數(shù)據(jù)庫操作、文件操作等报账。

      對比:

      https://note.youdao.com/yws/public/resource/7ad083d3cb19a5c9a95bc4a3445ca9d7/xmlnote/C96ECA80F051483E8C07D7E67156825F/3114

      補(bǔ)充:

      1. 阻塞隊(duì)列

        • ArrayBlockingQueue

          基于數(shù)組實(shí)現(xiàn)的有界的阻塞隊(duì)列,該隊(duì)列按照FIFO(先進(jìn)先出)原則對隊(duì)列中的元素進(jìn)行排序研底。

        • LinkedBlockingQueue

          基于鏈表實(shí)現(xiàn)的有界阻塞隊(duì)列,該隊(duì)列按照FIFO(先進(jìn)先出)原則對隊(duì)列中的元素進(jìn)行排序透罢,容量默認(rèn)為Integer.MAX_VALUE榜晦。當(dāng)線程池中線程數(shù)量達(dá)到corePoolSize后,再有新任務(wù)進(jìn)來羽圃,會一直存入該隊(duì)列乾胶,而不會去創(chuàng)建新線程直到maxPoolSize(幾乎不可能達(dá)到),因此使用該工作隊(duì)列時(shí)朽寞,參數(shù)maxPoolSize其實(shí)是不起作用的识窿。

        • SynchronousQueue

          內(nèi)部沒有任何容量的阻塞隊(duì)列。在它內(nèi)部沒有任何的緩存空間愁憔。對于SynchronousQueue中的數(shù)據(jù)元素只有當(dāng)我們試著取走的時(shí)候才可能存在腕扶,每一個(gè)put必須等待一個(gè)take操作

        • PriorityBlockingQueue

          具有優(yōu)先級的無限阻塞隊(duì)列。

        • DelayQueue

          是支持延時(shí)獲取元素的無界阻塞隊(duì)列吨掌。要求元素都實(shí)現(xiàn)Delayed接口半抱,通過執(zhí)行延時(shí)從隊(duì)列中提取任務(wù)脓恕,時(shí)間沒到任務(wù)取不出來。

      2. FixedThreadPool和SingleThreadExecutor:主要問題是堆積的請求處理隊(duì)列均采用LinkedBlockingQueue窿侈,可能會耗費(fèi)非常大的內(nèi)存炼幔,甚至OOM。

  5. 如何配置參數(shù)

《Java線程池實(shí)現(xiàn)原理及其在美團(tuán)業(yè)務(wù)中的實(shí)踐》

需要針對具體情況而具體處理史简,不同的任務(wù)類別應(yīng)采用不同規(guī)模的線程池乃秀,任務(wù)類別可劃分為CPU密集型任務(wù)、IO密集型任務(wù)和混合型任務(wù)圆兵。(N代表CPU個(gè)數(shù))

  • cpu密集型任務(wù)

    這種任務(wù)消耗的主要是 CPU 資源跺讯,可以將線程數(shù)設(shè)置為 N(CPU 核心數(shù))+1,比 CPU 核心數(shù)多出來的一個(gè)線程是為了防止線程偶發(fā)的缺頁中斷殉农,或者其它原因?qū)е碌娜蝿?wù)暫停而帶來的影響刀脏。一旦任務(wù)暫停,CPU 就會處于空閑狀態(tài)超凳,而在這種情況下多出來的一個(gè)線程就可以充分利用 CPU 的空閑時(shí)間愈污。

    當(dāng)線程數(shù)量太小,同一時(shí)間大量請求將被阻塞在線程隊(duì)列中排隊(duì)等待執(zhí)行線程轮傍,此時(shí) CPU 沒有得到充分利用暂雹;當(dāng)線程數(shù)量太大,被創(chuàng)建的執(zhí)行線程同時(shí)在爭取 CPU 資源创夜,又會導(dǎo)致大量的上下文切換杭跪,從而增加線程的執(zhí)行時(shí)間,影響了整體執(zhí)行效率挥下。通過測試可知揍魂,4~6 個(gè)線程數(shù)是最合適的。

  • io密集型任務(wù)

    這種任務(wù)應(yīng)用起來棚瘟,系統(tǒng)會用大部分的時(shí)間來處理 I/O 交互现斋,而線程在處理 I/O 的時(shí)間段內(nèi)不會占用 CPU 來處理,這時(shí)就可以將 CPU 交出給其它線程使用偎蘸。因此在 I/O 密集型任務(wù)的應(yīng)用中庄蹋,我們可以多配置一些線程,具體的計(jì)算方法是 2N限书。

    當(dāng)線程數(shù)量在 8 時(shí),線程平均執(zhí)行時(shí)間是最佳的倦西,這個(gè)線程數(shù)量和我們的計(jì)算公式所得的結(jié)果就差不多赁严。

  • 混合型

    可以拆分為CPU密集型任務(wù)和IO密集型任務(wù)粉铐,當(dāng)這兩類任務(wù)執(zhí)行時(shí)間相差無幾時(shí)卤档,通過拆分再執(zhí)行的吞吐率高于串行執(zhí)行的吞吐率,但若這兩類任務(wù)執(zhí)行時(shí)間有數(shù)據(jù)級的差距劝枣,那么沒有拆分的意義汤踏。

    線程池的核心線程數(shù)=(線程等待時(shí)間/線程CPU時(shí)間+1)*CPU核心數(shù);

  • 快速響應(yīng)用戶請求

    從用戶體驗(yàn)角度看舔腾,這個(gè)結(jié)果響應(yīng)的越快越好溪胶,如果一個(gè)頁面半天都刷不出,用戶可能就放棄查看這個(gè)商品了琢唾。而面向用戶的功能聚合通常非常復(fù)雜载荔,伴隨著調(diào)用與調(diào)用之間的級聯(lián)、多級級聯(lián)等情況采桃,業(yè)務(wù)開發(fā)同學(xué)往往會選擇使用線程池這種簡單的方式,將調(diào)用封裝成任務(wù)并行的執(zhí)行丘损,縮短總體響應(yīng)時(shí)間普办。另外,使用線程池也是有考量的徘钥,這種場景最重要的就是獲取最大的響應(yīng)速度去滿足用戶衔蹲,所以應(yīng)該不設(shè)置隊(duì)列去緩沖并發(fā)任務(wù),調(diào)高corePoolSize和maxPoolSize去盡可能創(chuàng)造多的線程快速執(zhí)行任務(wù)呈础。

  • 快速處理批量任務(wù)

    這種場景需要執(zhí)行大量的任務(wù)舆驶,我們也會希望任務(wù)執(zhí)行的越快越好。這種情況下而钞,也應(yīng)該使用多線程策略沙廉,并行計(jì)算。但與響應(yīng)速度優(yōu)先的場景區(qū)別在于臼节,這類場景任務(wù)量巨大撬陵,并不需要瞬時(shí)的完成,而是關(guān)注如何使用有限的資源网缝,盡可能在單位時(shí)間內(nèi)處理更多的任務(wù)巨税,也就是吞吐量優(yōu)先的問題。所以應(yīng)該設(shè)置隊(duì)列去緩沖并發(fā)任務(wù)粉臊,調(diào)整合適的corePoolSize去設(shè)置處理任務(wù)的線程數(shù)草添。在這里,設(shè)置的線程數(shù)過多可能還會引發(fā)線程上下文切換頻繁的問題扼仲,也會降低處理任務(wù)的速度远寸,降低吞吐量促王。

我們要提高線程池的處理能力蝇狼,一定要先保證一個(gè)合理的線程數(shù)量倡怎,也就是保證 CPU 處理線程的最大化。在此前提下颤专,我們再增大線程池隊(duì)列栖秕,通過隊(duì)列將來不及處理的線程緩存起來簇捍。在設(shè)置緩存隊(duì)列時(shí)暑塑,我們要盡量使用一個(gè)有界隊(duì)列锅必,以防因隊(duì)列過大而導(dǎo)致的內(nèi)存溢出問題搞隐。

  1. Java線程池ThreadPoolExecutor八大拒絕策略淺析

    內(nèi)置:4個(gè)

    • AbortPolicy(中止策略)

      public static class AbortPolicy implements RejectedExecutionHandler {
       public AbortPolicy() { }
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
       throw new RejectedExecutionException("Task " + r.toString() +
       " rejected from " +
       e.toString());
       }
       }
      功能:當(dāng)觸發(fā)拒絕策略時(shí)逢捺,直接拋出拒絕執(zhí)行的異常蒸甜,中止策略的意思也就是打斷當(dāng)前執(zhí)行流程
      
      使用場景:這個(gè)就沒有特殊的場景了柠新,但是一點(diǎn)要正確處理拋出的異常恨憎。
      
      ThreadPoolExecutor中默認(rèn)的策略就是AbortPolicy憔恳,ExecutorService接口的系列ThreadPoolExecutor因?yàn)槎紱]有顯示的設(shè)置拒絕策略钥组,所以默認(rèn)的都是這個(gè)程梦。
      
      但是請注意屿附,ExecutorService中的線程池實(shí)例隊(duì)列都是無界的挺份,也就是說把內(nèi)存撐爆了都不會觸發(fā)拒絕策略匀泊。當(dāng)自己自定義線程池實(shí)例時(shí)探赫,使用這個(gè)策略一定要處理好觸發(fā)策略時(shí)拋的異常,因?yàn)樗麜驍喈?dāng)前的執(zhí)行流程。
      
    • CallerRunsPolicy(調(diào)用者運(yùn)行策略)

      public static class CallerRunsPolicy implements RejectedExecutionHandler {
       public CallerRunsPolicy() { }
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
       if (!e.isShutdown()) {
       r.run();
       }
       }
       }
      功能:當(dāng)觸發(fā)拒絕策略時(shí)毛仪,只要線程池沒有關(guān)閉箱靴,就由提交任務(wù)的當(dāng)前線程處理衡怀。
      
      使用場景:一般在不允許失敗的抛杨、對性能要求不高怖现、并發(fā)量較小的場景下使用潘拨,因?yàn)榫€程池一般情況下不會關(guān)閉铁追,也就是提交的任務(wù)一定會被運(yùn)行琅束,但是由于是調(diào)用者線程自己執(zhí)行的透硝,當(dāng)多次提交任務(wù)時(shí)濒生,就會阻塞后續(xù)任務(wù)執(zhí)行罪治,性能和效率自然就慢了觉义。
      
    • DiscardOldestPolicy(棄老策略)

      public static class DiscardOldestPolicy implements RejectedExecutionHandler {
       public DiscardOldestPolicy() { }
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
       if (!e.isShutdown()) {
       e.getQueue().poll();
       e.execute(r);
       }
       }
       }
      功能:如果線程池未關(guān)閉晒骇,就彈出隊(duì)列頭部的元素洪囤,然后嘗試執(zhí)行當(dāng)前任務(wù)
      
      使用場景:這個(gè)策略還是會丟棄任務(wù)瘤缩,丟棄時(shí)也是毫無聲息剥啤,但是特點(diǎn)是丟棄的是老的未執(zhí)行的任務(wù),而且是待執(zhí)行優(yōu)先級較高的任務(wù)刻诊。
      
      基于這個(gè)特性坏逢,我能想到的場景就是,發(fā)布消息肖揣,和修改消息龙优,當(dāng)消息發(fā)布出去后彤断,還未執(zhí)行宰衙,此時(shí)更新的消息又來了供炼,這個(gè)時(shí)候未執(zhí)行的消息的版本比現(xiàn)在提交的消息版本要低就可以被丟棄了袋哼。因?yàn)殛?duì)列中還有可能存在消息版本更低的消息會排隊(duì)執(zhí)行闸衫,所以在真正處理消息的時(shí)候一定要做好消息的版本比較蔚出。
      
    • DiscardPolicy(丟棄策略)

      public static class DiscardPolicy implements RejectedExecutionHandler {
       public DiscardPolicy() { }
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
       }
       }
      功能:直接靜悄悄的丟棄這個(gè)任務(wù)衅胀,不觸發(fā)任何動作
      
      使用場景:如果你提交的任務(wù)無關(guān)緊要酥筝,你就可以使用它 嘿歌。因?yàn)樗褪莻€(gè)空實(shí)現(xiàn)宙帝,會悄無聲息的吞噬你的的任務(wù)步脓。所以這個(gè)策略基本上不用了
      

    第三方實(shí)現(xiàn)的拒絕策略

    • dubbo[?d?b??]中的線程拒絕策略

      public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {
       protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class);
       private final String threadName;
       private final URL url;
       private static volatile long lastPrintTime = 0;
       private static Semaphore guard = new Semaphore(1);
      
       public AbortPolicyWithReport(String threadName, URL url) {
       this.threadName = threadName;
       this.url = url;
       }
      
       @Override
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
       String msg = String.format("Thread pool is EXHAUSTED!" +
       " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," +
       " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!",
       threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),
       e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(),
       url.getProtocol(), url.getIp(), url.getPort());
       logger.warn(msg);
       dumpJStack();
       throw new RejectedExecutionException(msg);
       }
      
       private void dumpJStack() {
       //省略實(shí)現(xiàn)
       }
      }
      
      可以看到,當(dāng)dubbo的工作線程觸發(fā)了線程拒絕后鸳君,主要做了三個(gè)事情,原則就是盡量讓使用者清楚觸發(fā)線程拒絕策略的真實(shí)原因砸紊。
      
      1)輸出了一條警告級別的日志醉顽,日志內(nèi)容為線程池的詳細(xì)設(shè)置參數(shù)游添,以及線程池當(dāng)前的狀態(tài)否淤,還有當(dāng)前拒絕任務(wù)的一些詳細(xì)信息石抡≈螅可以說嗡贺,這條日志诫睬,使用dubbo的有過生產(chǎn)運(yùn)維經(jīng)驗(yàn)的或多或少是見過的摄凡,這個(gè)日志簡直就是日志打印的典范,其他的日志打印的典范還有spring钦扭。得益于這么詳細(xì)的日志其弊,可以很容易定位到問題所在
      
      2)輸出當(dāng)前線程堆棧詳情膀斋,這個(gè)太有用了概页,當(dāng)你通過上面的日志信息還不能定位問題時(shí)技掏,案發(fā)現(xiàn)場的dump線程上下文信息就是你發(fā)現(xiàn)問題的救命稻草哑梳。
      
      3)繼續(xù)拋出拒絕執(zhí)行異常鸠真,使本次任務(wù)失敗吠卷,這個(gè)繼承了JDK默認(rèn)拒絕策略的特性
      
    • Netty中的線程池拒絕策略

      private static final class NewThreadRunsPolicy implements RejectedExecutionHandler {
       NewThreadRunsPolicy() {
       super();
       }
      
       public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
       try {
       final Thread t = new Thread(r, "Temporary task executor");
       t.start();
       } catch (Throwable e) {
       throw new RejectedExecutionException(
       "Failed to start a new thread", e);
       }
       }
       }
      
      Netty中的實(shí)現(xiàn)很像JDK中的CallerRunsPolicy祭隔,舍不得丟棄任務(wù)疾渴。不同的是搞坝,CallerRunsPolicy是直接在調(diào)用者線程執(zhí)行的任務(wù)桩撮。而 Netty是新建了一個(gè)線程來處理的距境。
      
      所以,Netty的實(shí)現(xiàn)相較于調(diào)用者執(zhí)行策略的使用面就可以擴(kuò)展到支持高效率高性能的場景了粟按。但是也要注意一點(diǎn),Netty的實(shí)現(xiàn)里疼鸟,在創(chuàng)建線程時(shí)未做任何的判斷約束,也就是說只要系統(tǒng)還有資源就會創(chuàng)建新的線程來處理捌朴,直到new不出新的線程了洼怔,才會拋創(chuàng)建線程失敗的異常镣隶。
      
    • activeMq中的線程池拒絕策略

      new RejectedExecutionHandler() {
       @Override
       public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
       try {
       executor.getQueue().offer(r, 60, TimeUnit.SECONDS);
       } catch (InterruptedException e) {
       throw new RejectedExecutionException("Interrupted waiting for BrokerService.worker");
       }
      
       throw new RejectedExecutionException("Timed Out while attempting to enqueue Task.");
       }
       });
      
      activeMq中的策略屬于最大努力執(zhí)行任務(wù)型,當(dāng)觸發(fā)拒絕策略時(shí)域那,在嘗試一分鐘的時(shí)間重新將任務(wù)塞進(jìn)任務(wù)隊(duì)列琉雳,當(dāng)一分鐘超時(shí)還沒成功時(shí)友瘤,就拋出異常
      
    • pinpoint中的線程池拒絕策略

      public class RejectedExecutionHandlerChain implements RejectedExecutionHandler {
       private final RejectedExecutionHandler[] handlerChain;
      
       public static RejectedExecutionHandler build(List<RejectedExecutionHandler> chain) {
       Objects.requireNonNull(chain, "handlerChain must not be null");
       RejectedExecutionHandler[] handlerChain = chain.toArray(new RejectedExecutionHandler[0]);
       return new RejectedExecutionHandlerChain(handlerChain);
       }
      
       private RejectedExecutionHandlerChain(RejectedExecutionHandler[] handlerChain) {
       this.handlerChain = Objects.requireNonNull(handlerChain, "handlerChain must not be null");
       }
      
       @Override
       public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
       for (RejectedExecutionHandler rejectedExecutionHandler : handlerChain) {
       rejectedExecutionHandler.rejectedExecution(r, executor);
       }
       }
      }
      
      pinpoint的拒絕策略實(shí)現(xiàn)很有特點(diǎn)翠肘,和其他的實(shí)現(xiàn)都不同。他定義了一個(gè)拒絕策略鏈辫秧,包裝了一個(gè)拒絕策略列表香伴,當(dāng)觸發(fā)拒絕策略時(shí),會將策略鏈中的rejectedExecution依次執(zhí)行一遍乍钻。
      
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宫静,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子柿究,更是在濱河造成了極大的恐慌邮旷,老刑警劉巖办陷,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鲸鹦,居然都是意外死亡嵌戈,警方通過查閱死者的電腦和手機(jī)尉姨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門侄旬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汰蜘,“玉大人比被,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵薄嫡,是天一觀的道長毒姨。 經(jīng)常有香客問我俘枫,道長邓嘹,這世上最難降的妖魔是什么棚贾? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任琳轿,我火速辦了婚禮迹炼,結(jié)果婚禮上咱扣,老公的妹妹穿的比我還像新娘偏瓤。我一直安慰自己证舟,他們只是感情好抵知,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般揖庄。 火紅的嫁衣襯著肌膚如雪幕袱。 梳的紋絲不亂的頭發(fā)上擂煞,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機(jī)與錄音添坊,去河邊找鬼野蝇。 笑死澜躺,一個(gè)胖子當(dāng)著我的面吹牛耘戚,可吹牛的內(nèi)容都是我干的长捧。 我是一名探鬼主播串结,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼榨惠,長吁一口氣:“原來是場噩夢啊……” “哼简烤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起招拙,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后别凤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饰序,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年闻妓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菌羽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡由缆,死狀恐怖注祖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情均唉,我是刑警寧澤是晨,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站舔箭,受9級特大地震影響罩缴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜层扶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一箫章、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镜会,春花似錦檬寂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匾旭,卻和暖如春镣屹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背价涝。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工女蜈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人色瘩。 一個(gè)月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓鞭光,卻偏偏與公主長得像,于是被迫代替她去往敵國和親泞遗。 傳聞我的和親對象是個(gè)殘疾皇子惰许,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

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