<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>

          Java線程池必備知識(shí)點(diǎn):工作流程、常見參數(shù)、調(diào)優(yōu)、監(jiān)控

          共 7782字,需瀏覽 16分鐘

           ·

          2021-07-15 20:24

          1. Java的線程池

          ① 合理使用線程池的好處

          Java的線程池是運(yùn)用場(chǎng)景最多的并發(fā)框架,幾乎所有需要異步或者并發(fā)執(zhí)行任務(wù)的程序都可以使用線程池。

          合理使用線程池能帶來的好處:

          降低資源消耗。

          通過重復(fù)利用已經(jīng)創(chuàng)建的線程降低線程創(chuàng)建的和銷毀造成的消耗。例如,工作線程Woker會(huì)無線循環(huán)獲取阻塞隊(duì)列中的任務(wù)來執(zhí)行。

          提高響應(yīng)速度。

          當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。

          提高線程的可管理性。

          線程是稀缺資源,Java的線程池可以對(duì)線程資源進(jìn)行統(tǒng)一分配、調(diào)優(yōu)和監(jiān)控。

          ② 線程池的工作流程

          一個(gè)新的任務(wù)到線程池時(shí),線程池的處理流程如下:

          1. 線程池判斷核心線程池里的線程是否都在執(zhí)行任務(wù)。如果不是,創(chuàng)建一個(gè)新的工作線程來執(zhí)行任務(wù)。如果核心線程池里的線程都在執(zhí)行任務(wù),則進(jìn)入下個(gè)流程。
          2. 線程池判斷阻塞隊(duì)列是否已滿。如果阻塞隊(duì)列沒有滿,則將新提交的任務(wù)存儲(chǔ)在阻塞隊(duì)列中。如果阻塞隊(duì)列已滿,則進(jìn)入下個(gè)流程。
          3. 線程池判斷線程池里的線程是否都處于工作狀態(tài)。如果沒有,則創(chuàng)建一個(gè)新的工作線程來執(zhí)行任務(wù)。如果已滿,則交給飽和策略來處理這個(gè)任務(wù)。

          線程池的核心實(shí)現(xiàn)類是ThreadPoolExecutor類,用來執(zhí)行提交的任務(wù)。因此,任務(wù)提交到線程池時(shí),具體的處理流程是由ThreadPoolExecutor類的execute()方法去完成的。

          1. 如果當(dāng)前運(yùn)行的線程少于corePoolSize,則創(chuàng)建新的工作線程來執(zhí)行任務(wù)(執(zhí)行這一步驟需要獲取全局鎖)。
          2. 如果當(dāng)前運(yùn)行的線程大于或等于corePoolSize,而且BlockingQueue未滿,則將任務(wù)加入到BlockingQueue中。
          3. 如果BlockingQueue已滿,而且當(dāng)前運(yùn)行的線程小于maximumPoolSize,則創(chuàng)建新的工作線程來執(zhí)行任務(wù)(執(zhí)行這一步驟需要獲取全局鎖)。
          4. 如果當(dāng)前運(yùn)行的線程大于或等于maximumPoolSize,任務(wù)將被拒絕,并調(diào)用RejectExecutionHandler.rejectExecution()方法。即調(diào)用飽和策略對(duì)任務(wù)進(jìn)行處理。

          工作線程(Worker)

          線程池在創(chuàng)建線程時(shí),會(huì)將線程封裝成工作線程Woker。Woker在執(zhí)行完任務(wù)后,不是立即銷毀而是循環(huán)獲取阻塞隊(duì)列里的任務(wù)來執(zhí)行。

          ③ 線程池的創(chuàng)建(7個(gè)參數(shù))

          可以通過ThreadPoolExecutor來創(chuàng)建一個(gè)線程池:

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

          corePoolSize(線程池的基本大小):

          1. 提交一個(gè)任務(wù)到線程池時(shí),線程池會(huì)創(chuàng)建一個(gè)新的線程來執(zhí)行任務(wù)。注意:即使有空閑的基本線程能執(zhí)行該任務(wù),也會(huì)創(chuàng)建新的線程。
          2. 如果線程池中的線程數(shù)已經(jīng)大于或等于corePoolSize,則不會(huì)創(chuàng)建新的線程。
          3. 如果調(diào)用了線程池的prestartAllCoreThreads()方法,線程池會(huì)提前創(chuàng)建并啟動(dòng)所有基本線程。

          maximumPoolSize(線程池的最大數(shù)量):線程池允許創(chuàng)建的最大線程數(shù)。

          1. 阻塞隊(duì)列已滿,線程數(shù)小于maximumPoolSize便可以創(chuàng)建新的線程執(zhí)行任務(wù)。
          2. 如果使用無界的阻塞隊(duì)列,該參數(shù)沒有什么效果。

          workQueue(工作隊(duì)列):用于保存等待執(zhí)行的任務(wù)的阻塞隊(duì)列。

          1. ArrayBlockingQueue:基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列,按FIFO(先進(jìn)先出)原則對(duì)任務(wù)進(jìn)行排序。使用該隊(duì)列,線程池中能創(chuàng)建的最大線程數(shù)為maximumPoolSize。
          2. LinkedBlockingQueue:基于鏈表結(jié)構(gòu)的無界阻塞隊(duì)列,按FIFO(先進(jìn)先出)原則對(duì)任務(wù)進(jìn)行排序,吞吐量高于ArrayBlockingQueue。使用該隊(duì)列,線程池中能創(chuàng)建的最大線程數(shù)為corePoolSize。靜態(tài)工廠方法 Executor.newFixedThreadPool()使用了這個(gè)隊(duì)列。
          3. SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。添加任務(wù)的操作必須等到另一個(gè)線程的移除操作,否則添加操作一直處于阻塞狀態(tài)。靜態(tài)工廠方法 Executor.newCachedThreadPool()使用了這個(gè)隊(duì)列。
          4. PriorityBlokingQueue:一個(gè)支持優(yōu)先級(jí)的無界阻塞隊(duì)列。使用該隊(duì)列,線程池中能創(chuàng)建的最大線程數(shù)為corePoolSize。

          keepAliveTime(線程活動(dòng)保持時(shí)間):線程池的工作線程空閑后,保持存活的時(shí)間。如果任務(wù)多而且任務(wù)的執(zhí)行時(shí)間比較短,可以調(diào)大keepAliveTime,提高線程的利用率。

          unit(線程活動(dòng)保持時(shí)間的單位):可選單位有DAYS、HOURS、MINUTES、毫秒、微秒、納秒。

          handler(飽和策略,或者又稱拒絕策略):當(dāng)隊(duì)列和線程池都滿了,即線程池飽和了,必須采取一種策略處理提交的新任務(wù)。

          • AbortPolicy:無法處理新任務(wù)時(shí),直接拋出異常,這是默認(rèn)策略。
          • CallerRunsPolicy:用調(diào)用者所在的線程來執(zhí)行任務(wù)。
          • DiscardOldestPolicy:丟棄阻塞隊(duì)列中最靠前的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)。
          • DiscardPolicy:直接丟棄任務(wù)。

          threadFactory:構(gòu)建線程的工廠類

          總結(jié):

          1.常用的5個(gè),核心池、最大池、空閑時(shí)間、時(shí)間的單位、阻塞隊(duì)列;另外兩個(gè):拒絕策略、線程工廠類

          2.常見線程池的創(chuàng)建參數(shù)如下。PS: CachedThreadPool核心池為0,最大池為Integer.MAX_VALUE,相當(dāng)于只使用了最大池;其他線程池,核心池與最大池一樣大,因此相當(dāng)于只用了核心池。

          FixedThredPool: new ThreadExcutor(n, n, 0L, ms, new LinkedBlockingQueue<Runable>()
          SingleThreadExecutor: new ThreadExcutor(1, 1, 0L, ms, new LinkedBlockingQueue<Runable>())
          CachedTheadPool: new ThreadExcutor(0, max_valuem, 60L, s, new SynchronousQueue<Runnable>());
          ScheduledThreadPoolExcutor: ScheduledThreadPool, SingleThreadScheduledExecutor.

          3.如果使用的阻塞隊(duì)列為無界隊(duì)列,則永遠(yuǎn)不會(huì)調(diào)用拒絕策略,因?yàn)樵俣嗟娜蝿?wù)都可以放在隊(duì)列中。

          4.SynchronousQueue是不存儲(chǔ)任務(wù)的,新的任務(wù)要么立即被已有線程執(zhí)行,要么創(chuàng)建新的線程執(zhí)行。

          搜索公眾號(hào)Java知音,回復(fù)“2021”,送你一份Java面試題寶典)

          ④ 向線程池提交任務(wù)

          使用ThreadPoolEXecutor.executor()方法來提交任務(wù):

          public void execute(Runnable command) {
              // command為null,拋出NullPointerException
              if (command == null)
                  throw new NullPointerException();      
              int c = ctl.get();
              // 線程池中的線程數(shù)小于corePoolSize,創(chuàng)建新的線程
              if (workerCountOf(c) < corePoolSize) {
                  if (addWorker(command, true))// 創(chuàng)建工作線程
                      return;
                  c = ctl.get();
              }
              // 將任務(wù)添加到阻塞隊(duì)列,如果
              if (isRunning(c) && workQueue.offer(command)) {
                  int recheck = ctl.get();
                  if (! isRunning(recheck) && remove(command))
                      reject(command);
                  else if (workerCountOf(recheck) == 0)
                      addWorker(nullfalse);
              }// 阻塞隊(duì)列已滿,嘗試創(chuàng)建新的線程,如果超過maximumPoolSize,執(zhí)行handler.rejectExecution()
              else if (!addWorker(command, false))
                  reject(command);
          }

          ⑤ 線程池的五種運(yùn)行狀態(tài)

          RUNNING :  該狀態(tài)的線程池既能接受新提交的任務(wù),又能處理阻塞隊(duì)列中任務(wù)。

          SHUTDOWN: 該狀態(tài)的線程池不能接收新提交的任務(wù)但是能處理阻塞隊(duì)列中的任務(wù)。(政府服務(wù)大廳不在允許群眾拿號(hào)了,處理完手頭的和排隊(duì)的政務(wù)就下班。)

          • 處于 RUNNING 狀態(tài)時(shí),調(diào)用 shutdown()方法會(huì)使線程池進(jìn)入到該狀態(tài)。
          • 注意:finalize() 方法在執(zhí)行過程中也會(huì)隱式調(diào)用shutdown()方法。

          STOP:  該狀態(tài)的線程池不接受新提交的任務(wù),也不處理在阻塞隊(duì)列中的任務(wù),還會(huì)中斷正在執(zhí)行的任務(wù)。(政府服務(wù)大廳不再進(jìn)行服務(wù)了,拿號(hào)、排隊(duì)、以及手頭工作都停止了。)

          • 在線程池處于 RUNNING 或 SHUTDOWN 狀態(tài)時(shí),調(diào)用 shutdownNow() 方法會(huì)使線程池進(jìn)入到該狀態(tài);

          TIDYING:  如果所有的任務(wù)都已終止,workerCount (有效線程數(shù))=0 。

          • 線程池進(jìn)入該狀態(tài)后會(huì)調(diào)用 terminated() 鉤子方法進(jìn)入TERMINATED 狀態(tài)。

          TERMINATED: 在terminated()鉤子方法執(zhí)行完后進(jìn)入該狀態(tài),默認(rèn)terminated()鉤子方法中什么也沒有做。

          ⑥ 線程池的關(guān)閉(shutdown或者shutdownNow方法)

          可以通過調(diào)用線程池的shutdown或者shutdownNow方法來關(guān)閉線程池:遍歷線程池中工作線程,逐個(gè)調(diào)用interrupt方法來中斷線程。

          shutdown方法與shutdownNow的特點(diǎn):

          • shutdown方法將線程池的狀態(tài)設(shè)置為SHUTDOWN狀態(tài),只會(huì)中斷空閑的工作線程。
          • shutdownNow方法將線程池的狀態(tài)設(shè)置為STOP狀態(tài),會(huì)中斷所有工作線程,不管工作線程是否空閑。
          • 調(diào)用兩者中任何一種方法,都會(huì)使isShutdown方法的返回值為true;線程池中所有的任務(wù)都關(guān)閉后,isTerminated方法的返回值為true。
          • 通常使用shutdown方法關(guān)閉線程池,如果不要求任務(wù)一定要執(zhí)行完,則可以調(diào)用shutdownNow方法。

          2. java線程池的調(diào)優(yōu)以及監(jiān)控

          ① 線程池的調(diào)優(yōu)(線程池的合理配置)

          先從以下幾個(gè)角度分析任務(wù)的特性:

          • 任務(wù)的性質(zhì):CPU 密集型任務(wù)、IO 密集型任務(wù)和混合型任務(wù)。
          • 任務(wù)的優(yōu)先級(jí):高、中、低。
          • 任務(wù)的執(zhí)行時(shí)間:長(zhǎng)、中、短。
          • 任務(wù)的依賴性:是否依賴其他系統(tǒng)資源,如數(shù)據(jù)庫(kù)連接。

          任務(wù)性質(zhì)不同的任務(wù)可以用不同規(guī)模的線程池分開處理??梢酝ㄟ^ Runtime.getRuntime().availableProcessors() 方法獲得當(dāng)前設(shè)備的 CPU 個(gè)數(shù)。

          CPU 密集型任務(wù)配置 盡可能小的線程,如配置N^cpu+1個(gè)線程的線程池。

          IO 密集型任務(wù)則由于線程并不是一直在執(zhí)行任務(wù),則配置盡可能多的線程,如2*N^cpu。

          混合型任務(wù) 如果可以拆分,則將其拆分成一個(gè) CPU 密集型任務(wù)和一個(gè) IO 密集型任務(wù)。只要這兩個(gè)任務(wù)執(zhí)行的時(shí)間相差不是太大,那么分解后執(zhí)行的吞吐率要高于串行執(zhí)行的吞吐率;如果這兩個(gè)任務(wù)執(zhí)行時(shí)間相差太大,則沒必要進(jìn)行分解。

          • 優(yōu)先級(jí)不同的任務(wù)可以使用優(yōu)先級(jí)隊(duì)列 PriorityBlockingQueue 來處理,它可以讓優(yōu)先級(jí)高的任務(wù)先得到執(zhí)行。但是,如果一直有高優(yōu)先級(jí)的任務(wù)加入到阻塞隊(duì)列中,那么低優(yōu)先級(jí)的任務(wù)可能永遠(yuǎn)不能執(zhí)行。
          • 執(zhí)行時(shí)間不同的任務(wù)可以交給不同規(guī)模的線程池來處理,或者也可以使用優(yōu)先級(jí)隊(duì)列,讓執(zhí)行時(shí)間短的任務(wù)先執(zhí)行。
          • 依賴數(shù)據(jù)庫(kù)連接池的任務(wù),因?yàn)榫€程提交 SQL 后需要等待數(shù)據(jù)庫(kù)返回結(jié)果,線程數(shù)應(yīng)該設(shè)置得較大,這樣才能更好的利用 CPU。
          • 建議使用有界隊(duì)列,有界隊(duì)列能增加系統(tǒng)的穩(wěn)定性和預(yù)警能力??梢愿鶕?jù)需要設(shè)大一點(diǎn),比如幾千。使用無界隊(duì)列,線程池的隊(duì)列就會(huì)越來越大,有可能會(huì)撐滿內(nèi)存,導(dǎo)致整個(gè)系統(tǒng)不可用。② 線程池的監(jiān)控

          可以通過線程池提供的參數(shù)讀線程池進(jìn)行監(jiān)控,有以下屬性可以使用:

          • taskCount:線程池需要執(zhí)行的任務(wù)數(shù)量,包括已經(jīng)執(zhí)行完的、未執(zhí)行的和正在執(zhí)行的。
          • completedTaskCount:線程池在運(yùn)行過程中已完成的任務(wù)數(shù)量,completedTaskCount <= taskCount。
          • largestPoolSize:線程池曾經(jīng)創(chuàng)建過的最大線程數(shù)量,通過這個(gè)數(shù)據(jù)可以知道線程池是否滿過。如等于線程池的最大大小,則表示線程池曾經(jīng)滿了。
          • getPoolSize: 線程池的線程數(shù)量。如果線程池不銷毀的話,池里的線程不會(huì)自動(dòng)銷毀,所以線程池的線程數(shù)量只增不減。
          • getActiveCount:獲取活動(dòng)的線程數(shù)。

          通過繼承線程池并重寫線程池的 beforeExecute,afterExecute 和 terminated 方法,我們可以在任務(wù)執(zhí)行前,執(zhí)行后和線程池關(guān)閉前干一些事情。

          如監(jiān)控任務(wù)的平均執(zhí)行時(shí)間,最大執(zhí)行時(shí)間和最小執(zhí)行時(shí)間等。這幾個(gè)方法在線程池里是空方法,如:

          protected void beforeExecute(Thread t, Runnable r) { }

          3. Java線程池的常見問題

          1. 講講Java的線程池

          基礎(chǔ)講解:

          • 以ThreadPoolExecutor為切入點(diǎn),講解excute()方法中所體現(xiàn)的Java線程池運(yùn)行流程。
          • 工作線程Worker,它的循環(huán)工作特點(diǎn)
          • 如何新建線程池:7個(gè)參數(shù)(重點(diǎn)在阻塞隊(duì)列和飽和策略)
          進(jìn)階:
          • 線程池五個(gè)狀態(tài)的特點(diǎn)以及如何進(jìn)行狀態(tài)之間的切換:running、shutdown、stop、tidying、terminated。
          • 如何關(guān)閉線程:shutdown方法和shutdownNow方法的特點(diǎn)
          • 線程池的調(diào)優(yōu)(針對(duì)任務(wù)的不同特性 + 建議使用有界隊(duì)列)
          • 線程池的監(jiān)控參數(shù)以及可以重寫的方法。

          兩種主要的線程池類型:普通的線程池ThreadPoolExecutor,支持延遲或周期性執(zhí)行的任務(wù)的線程池ScheduledThreadPoolExcutor。

          講解ThreadPoolExcutor中5個(gè)常用參數(shù)+2個(gè)不常用參數(shù),包含的三種線程池:創(chuàng)建時(shí)的參數(shù)、運(yùn)行的流程、各自適合的場(chǎng)景。

          講解ScheduledThreadPoolExecutor的阻塞隊(duì)列的原理、如何更改任務(wù)的time。

          提供了五種定義好的線程池,都可以通過Executors工具類去調(diào)用,比如Executors.newFixedThreadPool(12)

          2. 具體的場(chǎng)景

          如果corePoolSize為x,maximumPoolSize為y,阻塞隊(duì)列為z,第w個(gè)任務(wù)進(jìn)來如何分配?

          3. 線程池如何進(jìn)行調(diào)優(yōu)?

          線程池的調(diào)優(yōu)(針對(duì)任務(wù)的不同特性 + 建議使用有界隊(duì)列)

          4. 線程池中的核心參數(shù)

          超過核心size怎么處理,隊(duì)列滿怎么處理,拒絕策略有哪些?(比較具體)

          END

          作者:曉之木初
          blog.csdn.net/u014454538/article/details/96910729
          瀏覽 36
          點(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>
                  日韩国产毛片一区二区三区无码精品 | 手机在线看片日韩 | 亚洲日韩欧美一区二区 | 又黄又爽视频 | 成人精品性爱 |