1. JAVA之線程池詳解

        共 4790字,需瀏覽 10分鐘

         ·

        2021-09-10 14:41

        作者:橘左京

        來(lái)源:SegmentFault 思否社區(qū)

        前言

        既然講到了線程池,那我們就先聊一下線程,線程為何物,每一個(gè)程序?yàn)閱为?dú)的一個(gè)進(jìn)程,如QQ、網(wǎng)易云,那我們?nèi)绾卫斫饩€程呢,其實(shí)在一個(gè)進(jìn)程內(nèi)至少包含一個(gè)線程來(lái)作為程序的最小執(zhí)行單位,如迅雷同時(shí)下載多個(gè)文件。

        線程池

        為什么要使用線程池?線程池顧名思義是由多個(gè)線程所組成,作用就是減少線程的建立與銷毀,與數(shù)據(jù)庫(kù)連接池相同概念,為了減少連接與釋放,從而降低消耗提升效率。

        適用場(chǎng)景

        很多小伙伴初識(shí)多線程,一直不明白多線程實(shí)際的應(yīng)用,它到底應(yīng)該應(yīng)用在什么地方,其實(shí)還是要根據(jù)你的具體業(yè)務(wù)來(lái)決定是否適用多線程,比如展示詳情頁(yè)需要花費(fèi)80ms,分八步查詢了不同表的數(shù)據(jù),每步占10ms,若是同步執(zhí)行就需要順序執(zhí)行組裝數(shù)據(jù),這時(shí)如果引入了多線程來(lái)進(jìn)行查詢,只需要10ms多的時(shí)間返回?cái)?shù)據(jù)。

        創(chuàng)建方式

        Executors類提供了六種不同的線程池創(chuàng)建方案,參數(shù)是方法默認(rèn)的,最終都是通過(guò)ThreadPoolExecutor實(shí)例。這種方式雖然簡(jiǎn)單方便,但是也有弊端,在開(kāi)發(fā)者不了解或無(wú)意中的使用可能會(huì)造成OOM。阿里手冊(cè)也明確的規(guī)定不準(zhǔn)直接使用Executors來(lái)創(chuàng)建線程池,要使用ThreadPoolExecutor去自定義線程池參數(shù)。

        1.Executors

        第一位登場(chǎng)的是Executors,我們先來(lái)展示一下它的方法,從圖中可以看到,它給出了六種創(chuàng)建線程的方式,請(qǐng)小伙伴們根據(jù)兩個(gè)一組順序閱讀。


        1)newCachedThreadPool

        可緩存型線程池。它的核心線程為0,但線程總數(shù)是Integer的最大值,意味著它是最大的。它使用SynchronousQueue來(lái)作為隊(duì)列,不會(huì)保留任務(wù),任務(wù)達(dá)到后直接創(chuàng)建線程,可能會(huì)造成OOM的發(fā)生。

        2)newFixedThreadPool

        定長(zhǎng)線程池。是一個(gè)可以控制并發(fā)數(shù)量的線程池,若任務(wù)到達(dá)后線程全部占用會(huì)加入到隊(duì)列當(dāng)中。它使用的是LinkedBlockingQueue作為隊(duì)列,是一個(gè)無(wú)界隊(duì)列,任務(wù)會(huì)源源不斷加入隊(duì)列,有可能造成OOM的發(fā)生。

        3)newScheduledThreadPool

        計(jì)劃型線程池。可以設(shè)置固定或延期執(zhí)行任務(wù),當(dāng)線程空閑時(shí),直接拿來(lái)使用,如果線程都被占用,則創(chuàng)建新的線程。它使用的是DelayedWorkQueue作為隊(duì)列,這種隊(duì)列能夠保證只有到了時(shí)間才會(huì)執(zhí)行任務(wù)。

        4)newSingleThreadExecutor

        單個(gè)線程的線程池。建立只有一個(gè)線程的線程池,若有多個(gè)任務(wù)進(jìn)來(lái),只有一個(gè)被執(zhí)行,其他進(jìn)入等待隊(duì)列,遵循先進(jìn)先出規(guī)則。它使用LinkedLockingQueue作為等待隊(duì)列,存在造成OOM的可能。

        5)newSingleThreadScheduledExecutor

        周期型執(zhí)行任務(wù)的單線程線程池。

        6)newWorkStealingPool

        工作竊取線程池。若有線程A、B,共分配了五個(gè)任務(wù),A分配到了四個(gè),B分配到一個(gè),若B執(zhí)行完任務(wù)A還沒(méi)有執(zhí)行完成,會(huì)主動(dòng)的去A竊取任務(wù)進(jìn)行執(zhí)行。

        2.ThreadPoolExecutor

        第二位登場(chǎng)的是ThreadPoolExecutor,它可以自定義不同的參數(shù)來(lái)達(dá)到目的,其中包含七個(gè)參數(shù),依次展示。

        public ThreadPoolExecutor(int corePoolSize,
                                      int maximumPoolSize,
                                      long keepAliveTime,
                                      TimeUnit unit,
                                      BlockingQueue<Runnable> workQueue,
                                      ThreadFactory threadFactory,
                                      RejectedExecutionHandler handler)


        **參數(shù)解析:
        1)corePoolSize:核心線程數(shù)**

        • 當(dāng)核心線程未被全部創(chuàng)建,即使有空閑線程也不會(huì)復(fù)用,繼續(xù)創(chuàng)建新的核心線程。

        • 核心線程為空閑狀態(tài)也不會(huì)回收,除非通過(guò)allowCoreThreadTimeOut(boolean value)設(shè)置為true

        • 可在項(xiàng)目初始化時(shí)調(diào)用prestartCoreThread()方法預(yù)創(chuàng)建線程,避免執(zhí)行任務(wù)時(shí)創(chuàng)建線程效率低。


        2)maximumPoolSize:線程總數(shù)

        核心線程+臨時(shí)線程的總數(shù),是線程總數(shù)的上線,在核心線程不夠用的情況下,會(huì)創(chuàng)建臨時(shí)線程協(xié)助處理任務(wù),臨時(shí)線程空閑超出規(guī)定時(shí)間則回收。

        3)keepAliveTime:超時(shí)時(shí)間

        4)unit:可指定過(guò)期時(shí)間的單位是秒、時(shí),分

        5)workQueue:工作隊(duì)列

        若沒(méi)有空閑線程,新的任務(wù)會(huì)加入到工作隊(duì)列等待執(zhí)行,隊(duì)列又分為有界與無(wú)界隊(duì)列,無(wú)界隊(duì)列其實(shí)也有上限,為Integer的最大值。

        6)threadFactory:線程工廠

        用于實(shí)現(xiàn)生成線程的方式,定義是否為守護(hù)線程,主要用于設(shè)置線程名稱。

        7)handler:拒絕策略

        當(dāng)?shù)却?duì)列已滿,就會(huì)執(zhí)行拒絕策略,提供了四種方式進(jìn)行選擇

        • ThreadPoolExecutor.AbortPlicy:拋出異常,默認(rèn)策略。

        • ThreadPoolExecutor.DiscardPolicy:直接丟棄任務(wù),但不拋出異常。

        • ThreadPoolExecutor.DsicardOldestPolicy:丟棄最早的任務(wù),將新任務(wù)加入隊(duì)列。

        • ThreadPoolExecutor.CallerRunsPolicy:由線程池所在的的線程處理任務(wù),自己處理自己的。


        使用ThreadPoolExecutor正確創(chuàng)建線程池

        static ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("橘左京").build();

          static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(30), factory, new ThreadPoolExecutor.AbortPolicy());

            public static class MyThread implements Runnable {
                @Override
                public void run() {
                    System.out.println("橘左京");
                }
            }
            public static void main(String[] args){
                MyThread myThread = new MyThread();
                executor.submit(myThread);
            }


        調(diào)用線程執(zhí)行有兩個(gè)方法,submit和execute,submit的作用是可以通過(guò)return獲取返回值,execute是無(wú)返回值的。



        點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開(kāi)更多互動(dòng)和交流,掃描下方”二維碼“或在“公眾號(hào)后臺(tái)回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~


        - END -



        瀏覽 40
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 天天射综合网站 | 性bbwbbwbbwbbw交 | 国产精品6666 | 婷婷五月天基地 | 日韩做爰成人片 |