<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          線程池知識(shí)點(diǎn)詳解

          共 2246字,需瀏覽 5分鐘

           ·

          2020-11-25 17:22

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ? 作者?|??萌新J

          來(lái)源 |? urlify.cn/A7BrIv

          66套java從入門到精通實(shí)戰(zhàn)課程分享

          文章正文:

          引入

          為什么使用線程池?

            在連接數(shù)少的情況下,對(duì)于需要線程的地方我們只需要直接新建線程來(lái)處理就可以了,但是在并發(fā)量高的場(chǎng)景下,頻繁的線程創(chuàng)建、銷毀是非常消耗資源的,所以針對(duì)于這樣的場(chǎng)景可以使用線程池,讓一開(kāi)始就創(chuàng)建好線程,在需要新連接進(jìn)來(lái)需要線程時(shí)就從線程池中拿一條執(zhí)行,完成后再將線程放回線程池,等到其他線程需要時(shí)再獲取就可以了,這樣可以有效提高系統(tǒng)整體的性能。?

          線程池的好處?適應(yīng)場(chǎng)景?

            好處:1、降低資源損耗   2、響應(yīng)速度快  3、方便線程管理  4、提供定時(shí)執(zhí)行、定期執(zhí)行、并發(fā)數(shù)控制等功能。適用場(chǎng)景:并發(fā)量大,IO操作多,需要頻繁創(chuàng)建線程的場(chǎng)景。

          阻塞隊(duì)列

            ?阻塞隊(duì)列是一個(gè)支持兩個(gè)附加操作的隊(duì)列,其本質(zhì)還是一個(gè)隊(duì)列,當(dāng)內(nèi)部存儲(chǔ)的數(shù)據(jù)量超過(guò)當(dāng)前阻塞隊(duì)列的容量時(shí),就會(huì)阻塞,停止接收新的數(shù)據(jù);同樣,如果內(nèi)部存儲(chǔ)的數(shù)據(jù)量變?yōu)?,那么也會(huì)阻塞,外界的數(shù)據(jù)請(qǐng)求也會(huì)不再接收。

          種類

          1、ArrayBlockingQueue:由數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列

          2、LinkedBlockingQueue:由鏈表組成的有界(但大小默認(rèn)值為Integer.MAX_Value)

          3、PriorityBlockingQueue:支持優(yōu)先級(jí)排序的無(wú)界阻塞隊(duì)列

          4、DelayQueue:使用優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)的延遲無(wú)界阻塞隊(duì)列

          5、SynchronizedQueue:不存儲(chǔ)元素的阻塞隊(duì)列,也即單個(gè)元素的隊(duì)列

          6、LinkedTransferQueue:由鏈表結(jié)構(gòu)組成的無(wú)界阻塞隊(duì)列

          7、LinkedBlockingDeque:由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列

          其中橘色的三種是常用的。其中 LinkedBlockingQueue 和 SynchronizedQueue 是兩個(gè)極端,SynchronizedQueue 是沒(méi)有容量的阻塞隊(duì)列,而 LinkedBlockingQueue 在未指定容量時(shí)可以看作是容量無(wú)窮大的阻塞隊(duì)列。


          核心方法

          方法類型拋出異常特殊值阻塞超時(shí)
          插入  add(e)offer(e)put(e)offer(e,time,unit)
          移除remove()poll()take()poll(time,unit)
          檢查element()peek()不可用不可用


          檢查是在有數(shù)據(jù)時(shí)返回隊(duì)列的第一個(gè)數(shù)據(jù),并不會(huì)從隊(duì)列中移除該數(shù)據(jù)。內(nèi)部使用 ReentrantLock 進(jìn)行同步控制的。

          拋出異常

          當(dāng)阻塞隊(duì)列滿時(shí),再往隊(duì)列里add插入元素會(huì)拋lllegalStateException:Queue full

          當(dāng)阻塞隊(duì)列空時(shí),再往隊(duì)列里remove移除元素會(huì)拋NoSuchElementException

          特殊值

          插入方法,成功true失敗false

          移除方法,成功返回出隊(duì)列的元素,沒(méi)有元素返回null

          阻塞

          隊(duì)列滿時(shí)put,隊(duì)列會(huì)一直阻塞直到put數(shù)據(jù)或者響應(yīng)中斷退出

          隊(duì)列為空時(shí)take,隊(duì)列會(huì)一直阻塞直到隊(duì)列可用

          超時(shí)當(dāng)隊(duì)列滿時(shí),隊(duì)列會(huì)阻塞生產(chǎn)者線程一定時(shí)間,超時(shí)后限時(shí)后生產(chǎn)者線程會(huì)退出

          ?

          線程池

          線程池的核心接口是 ExecutorService,它定義了線程池的各個(gè)基本抽象方法。

          ?

          執(zhí)行機(jī)制

          當(dāng)新的線程請(qǐng)求進(jìn)來(lái)時(shí),會(huì)先判斷核心線程數(shù)是否已滿,如果未滿則直接新建線程并執(zhí)行,執(zhí)行完將其放回線程池;

          ?如果已滿就再檢查隊(duì)列是否已滿,如果沒(méi)滿就將當(dāng)前線程請(qǐng)求加入阻塞隊(duì)列,等待空閑線程分配;

          ? 如果已滿就再檢查線程池當(dāng)前存在的線程數(shù)是否已達(dá)到規(guī)定的最大值,如果沒(méi)有達(dá)到就創(chuàng)建線程執(zhí)行;

                                                   如果達(dá)到就執(zhí)行對(duì)應(yīng)的飽和策略。

          其中的名詞下面會(huì)解釋。

          ?

          種類

          ThreadPoolExecutor

          首先看一下《阿里巴巴Java開(kāi)發(fā)手冊(cè)》中推薦的線程池創(chuàng)建,這也是線程池的最基本創(chuàng)建方式。那就是使用創(chuàng)建 ThreadPoolExecutor 對(duì)象來(lái)作為線程池,首先看一下它的構(gòu)造器

          ?可以看到它有四種構(gòu)造函數(shù),但前三種的實(shí)現(xiàn)本質(zhì)還是使用第四種實(shí)現(xiàn)的,只不過(guò)使用的都是對(duì)應(yīng)默認(rèn)配置而已。

          ?所以我們著重看一下第四個(gè)構(gòu)造函數(shù)。

          public?ThreadPoolExecutor(int?corePoolSize,
          ??????????????????????????????int?maximumPoolSize,
          ??????????????????????????????long?keepAliveTime,
          ??????????????????????????????TimeUnit?unit,
          ??????????????????????????????BlockingQueue?workQueue,
          ??????????????????????????????ThreadFactory?threadFactory,
          ??????????????????????????????RejectedExecutionHandler?handler)?{
          ????????if?(corePoolSize?????????????maximumPoolSize?<=?0?||
          ????????????maximumPoolSize?????????????keepAliveTime?????????????throw?new?IllegalArgumentException();
          ????????if?(workQueue?==?null?||?threadFactory?==?null?||?handler?==?null)
          ????????????throw?new?NullPointerException();
          ????????this.acc?=?System.getSecurityManager()?==?null??
          ????????????????null?:
          ????????????????AccessController.getContext();
          ????????this.corePoolSize?=?corePoolSize;
          ????????this.maximumPoolSize?=?maximumPoolSize;
          ????????this.workQueue?=?workQueue;
          ????????this.keepAliveTime?=?unit.toNanos(keepAliveTime);
          ????????this.threadFactory?=?threadFactory;
          ????????this.handler?=?handler;
          ????}

          內(nèi)容是對(duì)應(yīng)屬性的賦值,方法參數(shù)從左到右分別為核心線程數(shù)、線程池允許同時(shí)存在的最大線程數(shù)、線程的最大存活時(shí)間、最大存活時(shí)間的單位、阻塞隊(duì)列、線程工廠、拒絕策略。?

          ?  核心線程數(shù)、最大線程數(shù)、阻塞隊(duì)列和拒絕策略就是上面執(zhí)行機(jī)制中提到的。最大存活時(shí)間是指非核心線程在執(zhí)行完代碼后回到線程池,在經(jīng)過(guò)最大存活時(shí)間后仍然沒(méi)有任務(wù)分配給它,那么它就會(huì)被回收。核心線程則不會(huì)被回收,所以核心線程數(shù)就規(guī)定了線程池的最小線程數(shù)(當(dāng)前線程池剛剛被創(chuàng)建時(shí)為0,直到有線程請(qǐng)求進(jìn)來(lái)后才會(huì)開(kāi)始創(chuàng)建線程)。線程工廠是指定創(chuàng)建線程的工廠,這樣在創(chuàng)建線程可以更方便。拒絕策略是指在阻塞隊(duì)列滿以及線程池容納的線程數(shù)也達(dá)到最大線程池后執(zhí)行的策略。

          拒絕策略包括以下幾種:
          1、ThreadPoolExecutor.AbortPolicy:新任務(wù)提交直接拋出異常,RejectedExecutionException(默認(rèn))

          2、ThreadPoolExecutor.CallerRunsPolicy:即不拋棄任務(wù)也不拋出異常,而是將任務(wù)返回給調(diào)用者。不會(huì)在線程池中執(zhí)行,而是在調(diào)用executor方法的線程中執(zhí)行(也就是傳進(jìn)來(lái)的Runnble對(duì)象來(lái)創(chuàng)建線程啟動(dòng)運(yùn)行),會(huì)降低新任務(wù)的提交速度,影響程序的整體性能。

          3、ThreadPoolExecutor.DiscardPolicy:直接拋棄新提交的任務(wù)。

          4、ThreadPoolExecutor.DiscardOldestPolicy:拋棄最早加入阻塞隊(duì)列的請(qǐng)求。

          需要注意的是這些拒絕策略其實(shí)是 ThreadPoolExecutor 的內(nèi)部類。

          ?

          關(guān)于核心線程數(shù)的設(shè)置,可以參考下面的配置

          1、對(duì)于CPU密集型,也就是代碼大部分操作都是CPU去執(zhí)行計(jì)算處理的,不需要?jiǎng)?chuàng)建過(guò)多的線程,所以可以設(shè)置為?CPU核數(shù)+1

          2、對(duì)于IO密集型,因?yàn)镮O操作往往伴隨著線程的線程的使用,所以應(yīng)該設(shè)置大一些,所以可以設(shè)置為?CPU核數(shù)*2

          ?

          線程池的狀態(tài)

          1、Running。運(yùn)行中,線程池正常執(zhí)行,當(dāng)線程池被創(chuàng)建后就會(huì)進(jìn)入 Running 狀態(tài)。

          2、ShutDown。關(guān)閉,不會(huì)再接受新的線程請(qǐng)求,但是還是會(huì)處理阻塞隊(duì)列中的請(qǐng)求。當(dāng)調(diào)用對(duì)象的 shutdown 方法后就會(huì)進(jìn)入該狀態(tài)。

          3、Stop。停止,不會(huì)再接受新的線程請(qǐng)求,也不會(huì)再處理阻塞隊(duì)列中的請(qǐng)求。當(dāng)調(diào)用對(duì)象的 shutdownNow 方法后就會(huì)進(jìn)入該狀態(tài)。

          4、Tidying。進(jìn)入該狀態(tài)會(huì)開(kāi)始執(zhí)行線程池的 terminated 方法。在 ShutDown 狀態(tài)中阻塞隊(duì)列為空,同時(shí)線程池中的工作線程數(shù)為0時(shí)就會(huì)進(jìn)入該狀態(tài);在 Stop 狀態(tài)中工作線程數(shù)為0就會(huì)進(jìn)入該狀態(tài)。

          5、Terminated。終止。表示線程池正式停止工作。當(dāng)在 Tidying 狀態(tài)中執(zhí)行完 terminated 方法后就會(huì)進(jìn)入該狀態(tài)。

          ?

          常用方法

          1、execute(Runnable):處理 Ruunable 類型線程請(qǐng)求

          2、submit(Runnable)、submit(Callable):處理 Runnable 或者 Callable 類型的線程請(qǐng)求。submit 方法實(shí)現(xiàn)就是將 Callable對(duì)象轉(zhuǎn)成 FutureTask 類型對(duì)象再調(diào)用 execute 方法處理。

          3、shutdown():進(jìn)入 ShutDown 狀態(tài)

          4、shutdownNow():進(jìn)入 Stop 狀態(tài)

          5、terminated():線程池停止前執(zhí)行的方法,空方法,子類可以來(lái)重寫(xiě)自定義。

          6、getTaskCount():獲取線程池接收的任務(wù)總數(shù)

          7、getCompletedTaskCount():獲取線程池已完成的任務(wù)數(shù)

          8、getPoolSize():獲取線程池的線程數(shù)量

          9、getActiveCount():獲取線程池正在執(zhí)行的線程數(shù)

          ?

          ?


          其他封裝好的線程池

            對(duì)于直接創(chuàng)建 ThreadPoolExecutor 對(duì)象來(lái)實(shí)現(xiàn)線程池的創(chuàng)建,過(guò)程比較復(fù)雜,當(dāng)然在實(shí)際開(kāi)發(fā)中還是推薦這種方式,而在某些場(chǎng)景中則不需要定義這么規(guī)范的線程池,所以在 Executors 工具類中為我們封裝了幾種線程池,我們只需要調(diào)用方法就可以獲取對(duì)應(yīng)的線程池。

          1、Executors.newSingletonThreadExecutor。方法源碼如下。

          public?static?ExecutorService?newSingleThreadExecutor()?{
          ????????return?new?FinalizableDelegatedExecutorService
          ????????????(new?ThreadPoolExecutor(1,?1,
          ????????????????????????????????????0L,?TimeUnit.MILLISECONDS,
          ????????????????????????????????????new?LinkedBlockingQueue()));
          ????}

          可以看到這個(gè)線程池就是一個(gè)單線程的線程池,只能存儲(chǔ)一個(gè)線程,但是它的阻塞隊(duì)列是 LinkedBlockingQueue,所以意味著阻塞隊(duì)列的容量可以看作是無(wú)限大的。

          ?

          2、Executors.newFixedThreadPool(int)。方法源碼如下。

          public?static?ExecutorService?newFixedThreadPool(int?nThreads)?{
          ????????return?new?ThreadPoolExecutor(nThreads,?nThreads,
          ??????????????????????????????????????0L,?TimeUnit.MILLISECONDS,
          ??????????????????????????????????????new?LinkedBlockingQueue());
          ????}

          這個(gè)線程池的最大線程數(shù)是傳參值,核心線程數(shù)也是傳參值,這意味著在工作線程執(zhí)行完后回到線程池永遠(yuǎn)不會(huì)被回收,使用的阻塞隊(duì)列也是 LinkedBlockingQueue。

          ?

          3、Executors.newCachedThreadPool()。方法源碼如下。

          public?static?ExecutorService?newCachedThreadPool()?{
          ????????return?new?ThreadPoolExecutor(0,?Integer.MAX_VALUE,
          ??????????????????????????????????????60L,?TimeUnit.SECONDS,
          ??????????????????????????????????????new?SynchronousQueue());
          ????}

          這里的核心線程數(shù)是0,也就是線程池在空閑時(shí)會(huì)回收所有的線程,但是最大線程數(shù)是 Integer的最大范圍,所以可以看作可以同時(shí)包括無(wú)限大的線程,并且使用的阻塞隊(duì)列是 SynchronousQueue,所以當(dāng)線程請(qǐng)求進(jìn)來(lái)時(shí)總會(huì)立即創(chuàng)建線程執(zhí)行。

          ?

          4、Executors.newScheduledThreadPool(int)。方法源碼如下。

          public?static?ScheduledExecutorService?newScheduledThreadPool(int?corePoolSize)?{
          ????????return?new?ScheduledThreadPoolExecutor(corePoolSize);
          ????}




          ?public?ScheduledThreadPoolExecutor(int?corePoolSize)?{
          ????????super(corePoolSize,?Integer.MAX_VALUE,?0,?NANOSECONDS,
          ??????????????new?DelayedWorkQueue());
          ????}

          這個(gè)線程池的核心線程數(shù)是指定的參數(shù),最大線程數(shù)同樣是無(wú)限大,阻塞隊(duì)列是 DelayedWorkQueue(),默認(rèn)容量是16。

          ?

          上面四種封裝好的線程池都有缺陷,前兩個(gè)因?yàn)樽枞?duì)列是 LinkedBlockingQueue,所以在大量的線程請(qǐng)求進(jìn)來(lái)時(shí)大部分會(huì)存儲(chǔ)在阻塞隊(duì)列中,最終撐爆堆空間,拋出OOM;而后兩個(gè)因?yàn)樵试S的最大線程數(shù)是 Integer.MAX_VALUE,所以可以看作是無(wú)限大的,所以在大量的線程請(qǐng)求進(jìn)來(lái)時(shí)也會(huì)因?yàn)閯?chuàng)建過(guò)多的線程數(shù)而拋出OOM。所以這四種線程池需要慎用。





          粉絲福利:實(shí)戰(zhàn)springboot+CAS單點(diǎn)登錄系統(tǒng)視頻教程免費(fèi)領(lǐng)取

          ???

          ?長(zhǎng)按上方微信二維碼?2 秒
          即可獲取資料



          感謝點(diǎn)贊支持下哈?

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩欧美一级操逼大片 | 亚洲视频中文 | 亚洲看片 | 精品国产成人a在线观看 | 成人搞搞搞 |