1. 程式人生 > >JAVA執行緒池(ThreadPoolExecutor)原理分析與使用

JAVA執行緒池(ThreadPoolExecutor)原理分析與使用

在我們的開發中,“池”的概念並不罕見,有資料庫連線池、執行緒池、物件池、常量池等。下面我們主要針對執行緒池來一步一步揭開執行緒池的面紗。

使用執行緒池的好處

(1)降低資源消耗

可以重複利用自己建立的執行緒降低執行緒建立和銷燬造成的消耗。

(2)提高響應速度

當任務到達時,任務可以不需要等到執行緒建立就能立即執行。

(3)提高執行緒的可管理性

執行緒是稀缺資源,如果無限制地建立,不僅會消耗系統資源,還會降低系統的穩定性,使用執行緒池可以進行統一分配、調優和監控。

執行緒池的工作原理

首先,我們看下當一個新的任務提交到執行緒池之後,執行緒池是如何處理的?

(1)執行緒池判斷核心執行緒池裡的執行緒是否都在執行。如果不是,則建立一個新的工作執行緒來執行任務。如果核心執行緒池裡的執行緒都在執行任務,則執行第二步。

(2)執行緒池判斷工作佇列是否已經滿。如果工作佇列沒有滿,則將新提交的任務儲存在這個工作佇列裡進行等待。如果佇列滿了,則執行第三步。

(3)執行緒池判斷執行緒池的執行緒是否都處於工作狀態。如果沒有,則建立一個新的工作執行緒來執行任務。如果已經滿了,則交給飽和策略來處理這個任務。

執行緒池飽和策略

這裡提到了執行緒池的飽和策略,那我們就介紹下有哪些飽和策略?

(1)AbortPolicy    

為java執行緒池迷人的阻塞策略,不執行任務,而且直接丟擲一個執行時異常,切記ThreadPoolExecytor.execute需要try catch,否則程式會直接退出。

(2)DiscardPolicy

直接拋棄,任務不執行,空方法

(3)DiscardOldestPilicy

從佇列裡面拋棄head的一個任務,並再次execute 此task。

(4)CallerRunsPolicy

在呼叫execute的執行緒裡面執行此command,會阻塞入口。

(5)使用者 自定義拒絕策略(最常用)‘

實現RejectedExecutionHandler,並自己定義策略模式

下我們以ThreadPoolExecutor為例展示下執行緒池的工作流程圖:

1、如果當執行的執行緒少於corePoolSize,則建立新執行緒來執行任務(注意,執行這一步驟需要獲取全域性鎖)。

2、如果執行的執行緒等於或多餘corePoolSize,則將任務加入BlockingQueue。

3、如果無法將任務加入BlockingQueue(佇列已滿),則在非corePool中建立新的執行緒來處理任務(注意,執行這一步驟需要獲取全域性鎖)。

4、如果建立新的執行緒將使當前執行 的執行緒超出maximumPoolSize,任務將被拒絕,並呼叫RejectdeExecutionHandler.rejectedExecution()方法。

        ThreadPoolExecutor採取上述步驟的總體設計思路,是為了在執行execute()方法時,儘可能地避免獲取全域性鎖(那將會是一個嚴重的可伸縮瓶頸)。在ThreadPoolExecutor完成預熱之後(當前執行的執行緒數大於等於corePoolSize),幾乎所有的execute()方法呼叫都是執行步驟2,而步驟2不需要獲取全域性鎖。

關鍵方法原始碼分析

我們看看核心方法新增到執行緒池execute的原始碼如下:

下面我們繼續看看addWorker是如何實現的:

addWorker之後是runworker,第一次啟動會執行初始化傳進來的任務firstTask;然後會從workQueue中取任務執行,如果佇列為空則等到keepAliveTime這麼長時間


我們看一下getTask是如何執行的



下面我們看下processWorkerExit是如何工作的


tryTerminate

processWorkerExit方法中會嘗試呼叫TryTimeinate來終止執行緒池。這個方法任何可能導致執行緒池終止的動作後執行:比如比如減少workerCount或SHUTDOWN狀態下從佇列中移除任務。

shutdown這個方法會將runState置為SHUTDOWN,會終止所有空閒的執行緒。

shutdownNow方法將runState置為STOP。和shutdown方法的區別,這個方法會終止所有的執行緒。主要區別在於shutdown呼叫的interruptldleWorkers這個房,而showdownNow實際呼叫的是Worker類的interruptlfStarted方法:

他們的實現如下:

執行緒池的使用

執行緒池的建立

我們可以通過ThreadPoolExecutor來建立一個執行緒池

向執行緒池提交任務

可以使用兩個方法想執行緒池提交任務,分別為execute()和submit()方法。

execute()方法用於提交不需要返回值的任務,所以無法判斷任務是否被執行緒池執行成功,通過以下程式碼可知execute()方法輸入的任務是一個Runnable類的例項。

submit()方法用於提交需要返回值的任務,執行緒池會返回一個future型別的物件,通過這個future物件可以判斷任務是否執行成功,並且可以通過future的get()方法來獲取返回值,get()方法會阻塞當前執行緒直到任務完成,而get(long timeout,TimeUnit Unit)方法則會阻塞當前執行緒一段時間後立即返回,這時候有可能任務沒有執行完。

關閉執行緒池

可以通過呼叫執行緒池的shutdown或shutdownNow方法來關閉執行緒池。他們的原理是遍歷執行緒池中的工作執行緒,然後逐個呼叫執行緒的interrupt方法來中斷執行緒,所以無法響應中斷的任務可能永遠無法終止。但是他們存在一定的區別,showdownNow首先將執行緒池的狀態設定為STOP,然後嘗試停止所有正在執行或暫停任務的執行緒,寧返回等待執行任務的列表,而shutdown只是將執行緒池的狀態設定成SHUTDOWN狀態,然後中端所有沒有正在執行任務的執行緒。

只要呼叫了這兩個方法中的任意一個,isShutdown方法就會返回true,當所有的任務都已關閉後,才表示執行緒池關閉成功,這時呼叫isTerminaed方法會返回true。至於應該呼叫哪一種方法來關閉執行緒池,應該有提交到執行緒池的任務的特性決定,通常呼叫shutdown方法來關閉執行緒池,如果任務不一定執行完,則可以呼叫shutdownNow方法。

合理的配置執行緒池

要想合理的配置執行緒池,就必須首先分析任務特性,可以從以下幾個角度來分析。

1、任務的性質:CPU密集型任務、IO密集型任務和混合型任務。

2、任務的優先順序:高、中和低。

3、任務的執行時間:長、中和短。

4、任務的依賴性:是否依賴其他系統資源,如資料庫連線。

性質不同的任務可以用不同規模的執行緒池分開處理。CPU密集型任務應配置儘可能小的執行緒,如配置Ncpu+1個執行緒的執行緒池。由於IO密集型任務執行緒並不一定一直在執行人任務,則應配置儘可能多的執行緒,如2*Ncpu。混合型任務,如果可以拆分,將其拆分成一個CPU密集型任務和一個IO密集型任務,只要這兩個任務執行時間相差不是太大,那麼分解後執行的吞吐量將高於序列的吞吐量。如果這兩個任務執行時間相差太大,則沒有必要進行分解,可以通過

Runtime.getRuntime().availableProcessors()方法來獲取當前裝置的CPU個數。優先順序不通過的任務可以使用優先順序佇列PriorityBlockingQueue來處理。它可以讓優先順序有的任務先執行。

如果一直有優先順序高的任務提交到佇列中,那麼優先順序低的任務可能永遠不能執行。執行時間不同的任務可以交個不同規模的執行緒池來處理,或者可以使用優先順序佇列。執行讓執行時間短的任務先執行。依賴資料庫連線池的任務,因為執行緒提交SQL後需要等待資料庫返回結果,等待的時間越長,則CPU空閒時間就越長,那麼執行緒數應該設定得越大,這樣才能更好地利用CPU。

建議使用有界佇列。有界佇列能增加系統的穩定性和預警能力,可以根據需要設大一點兒,比如幾千。有時候我們系統裡後套任務執行緒池的佇列和執行緒池全滿了,不斷丟擲任務的異常,通過排查發現是資料庫出現了問題,導致執行SQL變得非常緩慢,因為後臺任務執行緒池裡的任務全是需要向資料庫查詢和插入資料的,所以導致執行緒池裡的工作執行緒全部阻塞,任務積壓線上程池裡。如果當時我們設定成無界佇列,那麼執行緒池的佇列就會越來越多,有可能會撐滿記憶體,導致整個系統不可用,而不是後臺任務出現問題。當然,我們的系統所有的任務是用單獨伺服器部署的,我們使用不同規模的執行緒池完成不同型別的任務,但是出現這樣的問題是也會影響到其他任務。

執行緒池的監控

如果在系統中大量使用執行緒池,則有必要對執行緒池進行監控,方便在出現問題時,可以根據執行緒池的使用狀態快速定位問題。可以通過執行緒池提供的引數進行監控,在監控執行緒池的時候可以使用一下屬性:

    taskCount:執行緒池需要執行的任務的數量

    completedTaskCount:執行緒池在執行過程中已完成的任務數量,小於或等於taskCount。

    largestPoolSize:執行緒池裡曾經建立過最大執行緒數量。通過這個資料可以知道執行緒池是否曾經滿過。如該數值等於執行緒池的最大大小,表示執行緒池曾經滿過。

    getPoolSize:執行緒池的執行緒數量。如果執行緒池不銷燬的話,執行緒池裡的執行緒不會自動銷燬,所以這個大小隻增不減

    getActiveCount:獲取活動 的執行緒數。

通過擴充套件執行緒池進行監控。可以通過繼承執行緒池來自定義執行緒池,重寫執行緒池的beforeExecute、afterExecute和terminated方法,也可以在任務執行前、執行後和執行緒池關閉前執行一些程式碼來進行監控。例如,監控任務的平均執行時間、最大執行時間和最小執行時間等。

相關推薦

JAVA執行ThreadPoolExecutor原理分析使用

在我們的開發中,“池”的概念並不罕見,有資料庫連線池、執行緒池、物件池、常量池等。下面我們主要針對執行緒池來一步一步揭開執行緒池的面紗。 使用執行緒池的好處 (1)降低資源消耗 可以重複利用自己建立的執行緒降低執行緒建立和銷燬造成的消耗。 (2)提高響應速度 當任務到達時

java執行newCachedThreadPool的使用

import java.util.concurrent.*; /** * java執行緒池的使用 * 這個【可快取執行緒池】,沒看出來有什麼用呢。。。 */ public class Exe

java-執行

一、為什麼使用執行緒池 重用執行緒池中的使用,減少建立線和銷燬程的的資源消耗和提高效能。 可以對執行緒進行管理與維護 二、執行緒池的建立   執行緒池的建立可以使用Executors類中的方法建立,可以參考常用的四種執行緒池的建立,下面來看J.U.C包下的T

java 執行1

ThreadPoolExecutor概述         ThreadPoolExecutor 下文簡稱 TPE ,我們使用它都是從Executror 這個類中的方法 : 1 public static Executo

Java執行ExecutorService使用

一、前提 /** * 執行緒執行demo,執行時打出執行緒id以及傳入執行緒中引數 */ public class ThreadRunner implements Runnable { private final SimpleDateForm

Java執行2——執行中的幾個重要方法詳解

【內容摘要】 在java中,如果需要進行多執行緒程式設計,可以採用java自帶的執行緒池來實現,執行緒池對於我們新手來說是一個非常好的選擇,因為我們可以不用關心執行緒池中執行緒是如何排程的,避免在多執行緒程式設計過程產生死鎖等問題。在瞭解執行緒池的使用前,本文

Java併發包原始碼學習之執行ThreadPoolExecutor原始碼分析

Java中使用執行緒池技術一般都是使用Executors這個工廠類,它提供了非常簡單方法來建立各種型別的執行緒池: public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService

Java併發4深入分析java執行框架及實現原理

先說說我個人對執行緒池的理解:執行緒池顧名思義是一個裝有很多執行緒的池子,這個池子維護著從執行緒建立到銷燬的怎個生命週期以及執行緒的分配,使用者只需要把任務提交給這個執行緒池而不用去關心執行緒池如何建立執行緒,執行緒池會自己給這些任務分配執行緒資源來完成任務。 java的E

java併發學習--執行

關於java中的執行緒池,我一開始覺得就是為了避免頻繁的建立和銷燬執行緒吧,先建立一定量的執行緒,然後再進行復用。但是要具體說一下如何做到的,自己又說不出一個一二三來了,這大概就是自己的學習習慣流於表面,不經常深入的結果吧。所以這裡決定系統的學習一下執行緒池的相關知識。   自己稍微總結了一下,

java執行有返回值和無返回值

無返回值: package ThreadPool2; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class test { public stat

深入理解Java執行--執行ThreadPool

在java多執行緒開發中,我們需要使用執行緒的時候一般是建立一個Thread物件,然後呼叫start()方法去執行執行緒操作。這樣做沒有什麼問題,但是如果我們有很多工需要多個執行緒來非同步執行的時候,在我們建立了很多執行緒的情況下,會造成很大的效能方面的問題。 1.大量的執行緒的建立和銷燬,

淺談java執行基於jdk1.8

多執行緒讓程式世界豐富多彩,也讓其錯綜複雜。對於執行緒的建立和銷燬成了一筆不小的開銷,為了減少這些開銷,出現了執行緒池。執行緒池對執行緒進行管理,對於需要使用多執行緒的你來說,只需要把你的任務丟給執行緒池就可以了。當你把任務丟給執行緒池的時候,它是如何處理的呢?

Android執行ThreadPoolExecutor類原始碼解析

使用ThreadPoolExecutor private final int CORE_POOL_SIZE = 4;//核心執行緒數 private final int MAX_POOL_SIZE = 5;//最大執行緒數 priv

Java執行執行

java.util.concurrent包是jdk1.5以後使用的執行緒庫,在jdk1.5之前主要使用java.lang和java.util中的類實現 package three.day.thread; import java.util.Random; import ja

Java併發程式設計:Java執行核心ThreadPoolExecutor的使用和原理分析

引出執行緒池 執行緒是併發程式設計的基礎,前面的文章裡,我們的例項基本都是基於執行緒開發作為例項,並且都是使用的時候就建立一個執行緒。這種方式比較簡單,但是存在一個問題,那就是執行緒的數量問題。 假設有一個系統比較複雜,需要的執行緒數很多,如果都是採用這種方式來建立執行緒的話,那麼就會極大的消耗系統資源。

Java執行執行

本例主要演示如何建立一個用於排程定時任務的執行緒池 package three.day.thread.my; import java.util.Random; import java.util.concurrent.Executors; import java.util.

java利用執行ExecutorService配合Callable和Future實現執行方法超時的阻斷

今天在專案開發中需要用到對執行方法加上時間控制,如果方法執行過長則跳出執行,廢話不說,直接上程式碼,用的是執行緒池配合Callable和Future方式對執行方法的超時阻斷。希望各位牛人指正  //啟用執行緒池 final ExecutorServic

Java執行執行4--執行的五種狀態

執行緒池的5種狀態:Running、ShutDown、Stop、Tidying、Terminated。 執行緒池各個狀態切換框架圖: 1、RUNNING (1) 狀態說明:執行緒池處在RUNNING狀態時,能夠接收新任務,以及對已新增的任務進行處理。

深入分析執行ThreadPoolExecutor常用方法

這4個類就是我們的主線。這裡我只顯示了public方法詳細介紹ThreadPoolExecutor的方法:方法shutdown() 和 shutdownNow()    shutdown :使當前未執行的執行緒繼續執行,而不再新增新的任務Task,該方法不會阻塞。    sh

java併發——執行執行機制和如何使用

合理利用執行緒池能夠帶來三個好處。 1、第一:降低資源消耗。通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。 2、第二:提高響應速度。當任務到達時,任務可以不需要的等到執行緒建立就能立即執行。 3、第三:提高執行緒的可管理性。執