線程池_初步認識_01

線程池
線程池_初步認識_01
  • 一、定義
    • 管理一組工作線程。
  • 二、好處
    • 1. 降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的消耗,比如內存;
    • 2. 提高響應速度。任務到達時,可以不需要等到線程創建就能執行;
    • 3. 提高線程的可管理性。通過線程池,實現對線程的統一分配,調優和監控;比如可以避免無線創建線程引起OutOfMemoryError。
  • 三、實現原理
    • 當向線程池提交一個任務之後,線程池是如何處理這個任務的呢?
      • 上圖就是線程池的主要處理流程;
      • ThreadPoolExecutor 執行execute()方法的四種情況(流程):
        • 1、如果當前運行的線程少於核心線程,則創建新線程來執行任務;(需要注意的是,此操作需要獲取全局鎖)
        • 2、如果運行的線程等於或對於核心線程,則將任務加入工作隊列中;
        • 3、如果工作隊列已滿,則創建新的線程(非核心線程)來處理任務;(需要獲取全局鎖)
        • 4、如果創建線程超出最大線程數,則任務被拒絕,調用飽和策略;(RejectedExecutionHandler.rejectedExecution())
        • 總結: 如此設計的原因?
          • 爲了在執行execute()方法時,儘可能地避免獲取全局鎖;因爲獲取全局鎖是一個嚴重可伸縮瓶頸。
        • 期望: 每次任務提交的時候,都是當前運行的線程數大於等於核心線程數,此時不用獲取全局鎖,即加入工作隊列。
  • 四、通過ThreadPoolExecutor創建線程池
    • public ThreadPoolExecutor(
      • int corePoolSize,
      • int maximumPoolSize,
      • long keepAliveTime,
      • TimeUnit unit,
      • BlockingQueue<Runnable> workQueue,
      • ThreadFactory threadFactory,
      • RejectedExecutionHandler defaultHandler)
    • 1. corePoolSize
      • 線程池的基本大小;如果要執行的任務數小於線程池基本大小,提交一個任務到線程池後,就會創建一個線程即便有空閒線程;否則,不創建,等待。
    • 2. maximumPoolSize
      • 線程池最大線程數。如果線程池中的線程數大於核心線程數並且隊列滿了,且線程數小於最大線程數,則會創建新的線程。
      • 如果maximumPoolSize與corePoolSize相等,即是固定大小線程池。
      • 如果使用無界隊列,該參數無效;
    • 3. keepAliveTime
      • 空閒線程存活時間。
      • 默認情況下,當線程池中的線程數大於corePoolSize時,keepAliveTime纔會起作用;當線程池中的線程空閒時,如果空閒時間等於keepAliveTime,線程會被銷燬,直到線程數等於核心線程數,避免浪費內存和句柄資源。
      • 當ThreadPoolExecutor的allowCoreThreadTimeOut變量設置爲true時,核心線程超時後也會被回收。
      • 如果任務多,並且每個任務的執行時間較短,增大時間,提高線程的利用率。
    • 4. unit
      • 時間單位
    • 5. BlockingQueue<Runnable> workQueue
      • 任務隊列,用於保存等待執行的任務的阻塞隊列。包括以下幾種:
        • 1.ArrayBlockingQueue:一個基於數組結構的有界阻塞隊列,FIFO(先進先出)原則對元素進行排序;
        • 2.LinkedBlockingQueue:一個基於鏈表結構的阻塞隊列,也是先進先出,一般情況下吞吐量高於ArrayBlockingQueue 。 FixedThreadPool()和SingleThreadExecutor()使用此隊列。
        • 3.SynchronousQueue:一個不存儲元素的阻塞隊列。上一個線程執行移除操作後,之後的插入操作才能執行,否則會一直處於阻塞狀態。吞吐量高於LinkedBlockingQueue。CachedThreadPool()使用此隊列。
        • 4.PriorityBlockingQueue:一個具有優先級的無限阻塞隊列。
    • 6. ThreadFactory threadFactory
      • 線程池創建線程使用的工廠;
      • 使用線程池創建線程的時候可以給予更有意義的名稱,便於定位問題。
    • 7. RejectedExecutionHandler defaultHandler
      • 線程池對拒絕任務的處理策略;發生在隊列和線程池都滿了的時候。默認是AbortPolicy,表示無法處理新任務時拋出異常。
        • 1.AbortPolicy : 直接拋出異常;
        • 2.CallerRunsPolicy: 只用調用者所在線程來運行任務;
        • 3.DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務;
        • 4.DiscardPolicy:不處理,不丟棄;
    • 自定義拒絕策略實現RejectedExecutionHandler接口,實現需要的場景。比如持久化拒絕的任務、記錄日誌等等。
  • 五、提交任務到線程池的方法
    • (一)、 通過execute
      • executorService.execute(newRunnable() {
        • @Override
        • public void run() {
          • System.out.println("我要開始運行了");
        • }
      • });
      • 提交不需要返回值的任務,無法判斷任務是否被線程池執行成功。
    • (二)、 通過submit
      • 1.通過提交Runnable
        • Future future =  executorService.submit(()->{});
      • 2.通過提交Callable
        • Future future =  executorService.submit(() -> {
          • return null;
        • });
      • 3.  Runnable 和 Callable 的區別
        • 1. 非常相似,這兩個接口都表示可以由線程或ExecutorService同時執行的任務。
        • 2. 不同之處是兩個接口內部執行的方法不同。
          • Runnable
            • public interface Runnable {
              • public void run();
            • }
          • Callable
            • public interface Callable{
              • public Object call() throws Exception;
            • }
        • - 可以看出,call()方法是有返回值的,而且可以引發異常。run沒有返回值,也不能引發異常(除非未經檢查的異常-RuntimeException的子類)。
      • 4.如果執行的任務需要返回結果,使用Callable。
    • (三)、兩種執行方法的區別
      • 1. execute 只能提交Runnable類型的任務;submit 除了可以提交Runnable類型的任務外,還可以提交Callable類型。
      • 2. execute 直接拋出任務執行的異常;submit 會捕獲,可以返回一個Future類型的對象,通過這個Future對象可以判斷任務是否執行成功,通過get方法獲取返回值,get()方法會阻塞當前線程直到任務完成,使用get(long timeout,TimeUnit unit)方法會阻塞當前線程一段時間後立即返回,這時候任務可能沒有執行完。
      • 3. submit 的頂級接口是ExecutorService;execute 的頂級接口是 Executor;
  • 六、獲取返回結果
    • (一)、 兩種 invokeAny() 和 invokeAll()
    • (二)、invokeAny
      • 1. 當任意一個任務得到結果後,會調用interrupt方法將其他的任務中斷;
      • 2. 部分任務失敗,會使用第一個成功的任務返回的結果;
      • 3. 任務全部失敗了,拋出Execption,invokeAny 方法將拋出ExecutionException。
    • (三)、invokeAll 返回所有任務的執行結果,該方法的執行效果也是阻塞執行的,要把所有的結果都取回時再繼續向下執行。
  • 七、關閉線程池
    • 1、shutdown()
      • 1. 將線程池的狀態設置成ShutDown;
      • 2. 中斷所有沒有正在執行任務的線程。在終止前允許執行以前提交的任務;
    • 2、shutdownNow()
      • 1. 將線程池的狀態設置成Stop;
      • 2. 阻止等待任務的啓動並試圖停止當前正在執行的任務,並返回等待執行任務的列表;不允許執行以前提交的任務。
    • 1、2 兩個方法的原理:
      • 遍歷線程池中的工作線程,然後逐個調用線程的interrupt方法來中斷線程,所以無法響應中斷的任務可能永遠無法終止。
      • 調用1、2方法後,isShutdown 方法返回true。當所有任務都已關閉,才表示線程池關閉成功,調用isTerminaed方法返回true。
    • 3、awaitTermination()
      • 1. 接收timeout和TimeUnit兩個參數,用於設定超時時間及單位。當等待超過設定時間時,會監測ExecutorService是否已經
      • 關閉,若關閉則返回true,否則返回false。一般情況下會和shutdown方法組合使用。
      • 在實際使用過程中, 使用shutdown()關閉,回收資源。如果有必要,可以在其後執行shutdownNow(),取消所有遺留的任務。
  • 八、配置線程池
    • 角度:
      • 1. 任務的性質: CPU密集型任務、IO密集型任務和混合型任務。
      • 2. 任務的優先級: 高、中與低;
      • 3. 任務的執行時間: 長、短;
      • 4. 任務的依賴性: 是否依賴其他系統資源,如數據庫連接。
    • 選擇:
      • 性質不同的任務可以用不同規模的線程池分開處理;
      • CPU 密集型任務應配置儘可能小的線程,如cpu數+1 的線程池;
      • IO密集型任務線程並不是一直在執行,則應配置極可能多的線程;
      • 優先級不同的任務可以使用優先級隊列PriorityBlockingQueue來處理,它可以讓優先級高的任務先執行;
        • 如果一直有優先級高的任務提交到隊列裏,那麼優先級低的任務可能永遠不能執行。
      • 依賴數據庫連接的任務,因爲線程提交SQL後需要等待數據庫返回結果,等待的時間越長,則CPU空閒時間就越長,那麼線程數應該設置的越大,這樣才能更好低利用CPU。
    • 建議:
      • 有界隊列;有界隊列可以增加系統的穩定性和預警能力。
      • 體現:
        • 任務線程池的隊列和線程池滿了,不斷拋出拋棄任務的異常,通過排查是數據庫出現問題,導致執行sql的速度變慢,由於後臺任務線程池裏的任務都是需要向數據庫插入數據的和查詢,所以導致線程池裏的工作線程全部阻塞,任務積壓在線程池裏。如果是無界隊列,線程池中的隊列越來越多,可能撐爆內存,導致系統不可用。
  • 八、例子
    • ExecutorService executorService = Executors.newSingleThreadExecutor();
      • Set<Callable<String>> callables = newHashSet<Callable<String>>();
        • callables.add(() -> {
          • return "Task1";
        • }});
        • callables.add(() -> {
          • return "Task2";
        • }});
      • List<Future<String>> futures =executorService.invokeAll(callables);
        • for(Future<String> future : futures){
          • System.out.println("future.get = " + future.get());
        • }
      • executorService.shutdown();
      • while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
        • System.out.println("線程池沒有關閉");
      • }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章