《java並發編程實戰》讀書筆記5--任務執行, Executor框架
6.1 在線程中執行任務
第一步要找出清晰的任務邊界。大多數服務器應用程序都提供了一種自然的任務邊界選擇方式:以獨立的請求為邊界。
-6.6.1 串行地執行任務
最簡單的任務調度策略是在單個線程中串行地執行各項任務。
雖然簡單明了,但是每次只能處理一個請求。當服務器正在處理請求時,新到來的連接必須等待直到請求處理完成,然後服務器將再次調用accept。
-6.1.2 顯示地為任務創建線程
-6.1.3 無限制創建線程的不足
(1) 線程生命周期的開銷非常高
(2) 資源消耗
(3) 穩定性: 在可創建線程的數量上存在一個限制,這個限制值隨著平臺的不同而不同並受到多個因素制約,包括JVM啟動參數,Thread構造函數中請求的棧大小,以及底層操作系統對線程的限制等。
6.2 Executor框架
java.util.concurrent提供了一種靈活的線程池作為Executor框架的一部分。
Executor基於生產者-消費者模式,提交任務的操作相當於生產者,執行任務的線程則相當於消費者。
-6.2.1 示例:基於Executor的web服務器
通過使用Executor,將請求任務的提交與任務的實際執行解耦,並且只需采用另一種不同的Executor實現,就可以輕松改變服務器的行為:
-6.6.2 執行策略
-6.2.3 線程池
通過調用Executor中的靜態工廠方法來創建一個線程池:
-6.2.4 Executor生命周期
雖然知道如何創建一個Executor,但現在卻不知道如何關閉它。JVM只有在所有的非守護線程全部終止後才會退出。因此,如果無法正確地關閉Executor,那麽JVM將無法結束。Executor擴展了ExecutorService接口,添加了一些用於生命周期管理方法:
-6.2.5 延遲任務與周期任務
Timer類負責管理延遲任務以及周期任務,但是存在一些缺陷,因此考慮使用ScheduledThreadPoolExecutor來代替它。
ps:這裏吐槽下這本書的漢化版,102頁的圖順序貼錯了,漢化版的質量實在是不敢恭維。
6.3 找出可利用的並行性
若使用Executor,必須將任務表述為一個Runnable。 有時候任務邊界並非顯而易見的。在單個用戶請求中仍可能存在可發掘的並行性。
-6.3.1 示例:串行的頁面渲染器
先繪制文本元素,同時為圖像預留出矩形占位空間,在處理完了第一篇文本後,程序再開始下載圖像,並將它們繪制到相應的占位空間中:
我們可以就將問題分解為多個獨立的任務並發執行,從而獲得更高的CPU利用率和響應靈敏度。
-6.3.2 攜帶結果的任務Callable和Future
ExecutorService中的所有方法都將返回一個Future,從而將一個Runnable或Callable提交給Executor,並得到一個Future來獲得任務的執行結果或取消任務。
-6.3.3 示例:使用Future實現頁面渲染器
為了使頁面渲染器實現更高的並發性,首先將渲染任務分解成兩個任務:一個是渲染所有的文本,另一個是下載所有圖像:
-6.3.4 在異構任務並行中存在的局限
-6.3.5 CompletionService:Executor與BlockingQueue
CompletionService將Executor和BlockingQueue的功能融合在一起,可已將Callable任務提交給它來執行,然後使用類似於隊列操作的take和poll方法來獲得以完成的結果,這些結果在完成時將被封裝成Future。ExecutorCompletionService實現了CompletionService,並將計算部分委托給一個Executor。CompletionService和ExecutorCompletionService詳解
-6.3.6 示例:使用CompletionService實現頁面渲染器
為每一幅圖像的下載都創建一個獨立的任務,並在線程池中執行它們。此外,通過從CompletionService中獲取結果以及使每張圖片在下載完成後立刻顯示出來,能使用戶獲得一個更加動態和響應的用戶界面:
-6.3.7 為任務設置時限
-6.3.8 示例:旅行預定門戶網站
小結:
《java並發編程實戰》讀書筆記5--任務執行, Executor框架