深刻淺出 Java Concurrency (28): 線程池 part 1 簡介[轉]

從這一節開始正式進入線程池的部分。其實整個體系已經拖了很長的時間,所以後面的章節會加快速度,甚至只是一個半成品或者簡單化,之後有時間的慢慢補充、完善。html

其實線程池是併發包裏面很重要的一部分,在實際狀況中也是使用不少的一個重要組件。java

下圖描述的是線程池API的一部分。廣義上的完整線程池可能還包括Thread/Runnable、Timer/TimerTask等部分。這裏只介紹主要的和高級的API以及架構和原理。多線程

 

 

ThreadPool2

 

大多數併發應用程序是圍繞執行任務(Task)進行管理的。所謂任務就是抽象、離散的工做單元(unit of work)。把一個應用程序的工做(work)分離到任務中,能夠簡化程序的管理;這種分離還在不一樣事物間劃分了天然的分界線,能夠方便程序在出現錯誤時進行恢復;同時這種分離還能夠爲並行工做提供一個天然的結構,有利於提升程序的併發性。[1]架構

 

併發執行任務的一個很重要前提是拆分任務。把一個大的過程或者任務拆分紅不少小的工做單元,每個工做單元可能相關、也可能無關,這些單元在必定程度上能夠充分利用CPU的特性併發的執行,從而提升併發性(性能、響應時間、吞吐量等)。併發

所謂的任務拆分就是肯定每個執行任務(工做單元)的邊界。理想狀況下獨立的工做單元有最大的吞吐量,這些工做單元不依賴於其它工做單元的狀態、結果或者其餘資源等。所以將任務儘量的拆分紅一個個獨立的工做單元有利於提升程序的併發性。負載均衡

對於有依賴關係以及資源競爭的工做單元就涉及到任務的調度和負載均衡。工做單元的狀態、結果或者其餘資源等有關聯的工做單元就須要有一個整體的調度者來協調資源和執行順序。一樣在有限的資源狀況下,大量的任務也須要一個協調各個工做單元的調度者。這就涉及到任務執行的策略問題。異步

任務的執行策略包括4W3H部分:高併發

  • 任務在什麼(What)線程中執行
  • 任務以什麼(What)順序執行(FIFO/LIFO/優先級等)
  • 同時有多少個(How Many)任務併發執行
  • 容許有多少個(How Many)個任務進入執行隊列
  • 系統過載時選擇放棄哪個(Which)任務,如何(How)通知應用程序這個動做
  • 任務執行的開始、結束應該作什麼(What)處理

在後面的章節中會詳細分寫這些策略是如何實現的。咱們先來簡單回答些如何知足上面的條件。性能

  1. 首先明確必定是在Java裏面能夠供使用者調用的啓動線程類是Thread。所以Runnable或者Timer/TimerTask等都是要依賴Thread來啓動的,所以在ThreadPool裏面一樣也是靠Thread來啓動多線程的。
  2. 默認狀況下Runnable接口執行完畢後是不能拿到執行結果的,所以在ThreadPool裏就定義了一個Callable接口來處理執行結果。
  3. 爲了異步阻塞的獲取結果,Future能夠幫助調用線程獲取執行結果。
  4. Executor解決了向線程池提交任務的入口問題,同時ScheduledExecutorService解決了如何進行重複調用任務的問題。
  5. CompletionService解決了如何按照執行完畢的順序獲取結果的問題,這在某些狀況下能夠提升任務執行的併發,調用線程沒必要在長時間任務上等待過多時間。
  6. 顯然線程的數量是有限的,並且也不宜過多,所以合適的任務隊列是必不可少的,BlockingQueue的容量正好能夠解決此問題。
  7. 固定任務容量就意味着在容量滿了之後須要必定的策略來處理過多的任務(新任務),RejectedExecutionHandler正好解決此問題。
  8. 必定時間內阻塞就意味着有超時,所以TimeoutException就是爲了描述這種現象。TimeUnit是爲了描述超時時間方便的一個時間單元枚舉類。
  9. 有上述問題就意味了配置一個合適的線程池是很複雜的,所以Executors默認的一些線程池配置能夠減小這個操做。

線程池的基本策略大體就這些,從下一節開始就從線程池的基本原理和執行方法開始描述。.net

 

[1] Java Concurrency in Practice

相關文章
相關標籤/搜索