Java併發程式設計實戰系列6之任務執行(Task Execution)
1. 線上程中執行任務
1.1 序列的執行任務
這是最經典的一個最簡單的Socket server的例子,伺服器的資源利用率非常低,因為單執行緒在等待I/O操作完成時,CPU處於空閒狀態。從而阻塞了當前請求的延遲,還徹底阻止了其他等待中的請求被處理。
public class SingleThreadWebServer { public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(80); while (true) { Socket connection = socket.accept(); handleRequest(connection); } } private static void handleRequest(Socket connection) { // request-handling logic here } }
1.2 顯式地為任務建立執行緒
任務處理從主執行緒中分離出來,主迴圈可以快速等待下一個連線,提高響應性。同時任務可以並行處理了,吞吐量也提高了。
public class ThreadPerTaskWebServer { public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(80); while (true) { final Socket connection = socket.accept(); Runnable task = new Runnable() { public void run() { handleRequest(connection); } }; new Thread(task).start(); } } private static void handleRequest(Socket connection) { // request-handling logic here } }
1.3 無限制建立執行緒的不足
執行緒的生命週期開銷非常高
資源消耗。大量的空閒執行緒佔用記憶體,給GC帶來壓力,同時執行緒數量過多,競爭CPU資源開銷太大。
穩定性。容易引起GC問題,甚至OOM
2 Executor框架
任務就是一組邏輯工作單元(unit of work),而執行緒則是使任務非同步執行的機制。
Executor介面,是代替Thread來做非同步執行的入口,介面雖然簡單,卻為非常靈活強大的非同步任務執行框架提供了基礎。
提供了一種標準的方法將任務的提交與執行過程解耦,並用Runnable(無返回時)或者Callable(有返回值)表示任務。
Executor基於生產者-消費者模式
提交任務/執行任務分別相當於生產者/消費者,通常是最簡單的實現生產者-消費者設計的方式了
2.1 基於Executor改造後的樣例如下
將請求處理任務的提交與任務的實際執行解耦,並且只需採用另一種不同的Executor實現,就可以改變伺服器的行為,其影響遠遠小於修改任務提交方式帶來的影響
2.2 執行策略
這一節主要介紹做一個Executor框架需要靠那些點?
在什麼執行緒中執行任務?
任務按照什麼順序執行?FIFO/LIFO/優先順序
有多少個任務可以併發執行?
佇列中允許多少個任務等待?
如果系統過載了要拒絕一個任務,那麼選擇拒絕哪一個?如何通知客戶端任務被拒絕了?
在執行任務過程中能不能有些別的動作before/after或者回撥?
各種執行策略都是一種資源管理工具,最佳的策略取決於可用的計算資源以及對服務質量的要求。
因此每當看到
new Thread(runnable).start();
並且希望有一種靈活的執行策略的時候,請考慮使用Executor來代替
2.3 執行緒池
線上程池中執行任務比為每個任務分配一個執行緒優勢明顯:
重用執行緒,減少開銷。
延遲低,執行緒是等待任務到達。
最大化挖掘系統資源以及保證穩定性。CPU忙碌但是又不會出現執行緒競爭資源而耗盡記憶體或者失敗的情況。
Executors可以看做一個工廠,提供如下幾種Executor的建立:
newCachedThreadPool newFixedThreadPool newSingleThreadExecutor newScheduledThreadPool
2.4 Executor的生命週期
為解決執行服務的生命週期問題,Executor擴充套件了ExecutorService介面,新增了一些用於生命週期管理的方法
一個優雅停止的例子:
增加生命週期擴充套件Web伺服器的功能
呼叫stop
客戶端請求形式
關閉
2.5 延遲任務與週期任務
使用Timer的弊端在於
如果某個任務執行時間過長,那麼將破壞其他TimerTask的定時精確性(執行所有定時任務時只會建立一個執行緒),只支援基於絕對時間的排程機制,所以對系統時鐘變化敏感
TimerTask丟擲未檢查異常後就會終止定時執行緒(不會捕獲異常)
更加合理的做法是使用ScheduledThreadPoolExecutor,只支援基於相對時間的排程
它是DelayQueue的應用場景
3 找出可利用的並行性
3.1 攜帶結果的任務Callable和Future
Executor框架支援Runnable,同時也支援Callable(它將返回一個值或者丟擲一個異常)
在Executor框架中,已提交但是尚未開始的任務可以取消,但是對於那些已經開始執行的任務,只有他們能響應中斷時,才能取消。
Future非常實用,他的API如下
內部get的阻塞是靠LockSupport.park來做的,在任務完成後Executor回撥finishCompletion方法會依次喚醒被阻塞的執行緒。
ExecutorService的submit方法接受Runnable和Callable,返回一個Future。ThreadPoolExecutor框架留了一個口子,子類可以重寫newTaskFor來決定建立什麼Future的實現,預設是FutureTask類。
3.2 示例:使用Future實現頁面的渲染器
3.3 CompletionService: Executor與BlockingQueue
計算完成後FutureTask會呼叫done方法,而CompletionService整合了FutureTask,對於計算完畢的結果直接放在自己維護的BlockingQueue裡面,這樣上層呼叫者就可以一個個take或者poll出來。
3.3 示例:使用CompletionService提高渲染效能
void renderPage(CharSequence source) { final List<ImageInfo> info = scanForImageInfo(source); CompletionService<ImageData> completionService = new ExecutorCompletionService<ImageData>(executor); for (final ImageInfo imageInfo : info) completionService.submit(new Callable<ImageData>() { public ImageData call() { return imageInfo.downloadImage(); } }); renderText(source); try { for (int t = 0, n = info.size(); t < n; t++) { Future<ImageData> f = completionService.take(); ImageData imageData = f.get(); renderImage(imageData); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (ExecutionException e) { throw launderThrowable(e.getCause()); } }
Future的get支援timeout。
使用invokeAll方法提交List<Callable>
,返回一個List<Future>
作者:芥末無疆sss
連結:
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4548/viewspace-2816167/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 《JAVA併發程式設計實戰》任務執行Java程式設計
- Java併發實戰(六) 任務執行Java
- 《Java併發程式設計實戰》學習筆記 任務執行和取消關閉Java程式設計筆記
- 【面試實戰】# 併發程式設計之執行緒池配置實戰面試程式設計執行緒
- Java併發系列—併發程式設計挑戰Java程式設計
- Java併發程式設計實戰(5)- 執行緒生命週期Java程式設計執行緒
- Java併發程式設計實戰系列16之Java記憶體模型(JMM)Java程式設計記憶體模型
- Java併發程式設計實戰Java程式設計
- Java併發程式設計:Java執行緒Java程式設計執行緒
- 併發程式設計之:執行緒程式設計執行緒
- Java併發程式設計實戰--FutureTaskJava程式設計
- Java併發程式設計之執行緒安全、執行緒通訊Java程式設計執行緒
- Java併發程式設計序列之執行緒狀態Java程式設計執行緒
- 技術問答集錦(12)併發程式設計-任務執行程式設計
- Java併發程式設計之執行緒篇之執行緒中斷(三)Java程式設計執行緒
- Java併發程式設計之執行緒篇之執行緒簡介(二)Java程式設計執行緒
- .NET併發程式設計-任務函式並行程式設計函式並行
- java併發程式設計——執行緒池Java程式設計執行緒
- java併發程式設計——執行緒同步Java程式設計執行緒
- Java併發程式設計筆記6:執行緒池的使用Java程式設計筆記執行緒
- Java併發程式設計實戰--Amdahl定律Java程式設計
- Java併發程式設計之執行緒篇之執行緒的由來(一)Java程式設計執行緒
- 《Java併發程式設計實戰》 第二章:執行緒安全性Java程式設計執行緒
- 實現一個併發任務執行框架框架
- java程式設計思想之併發(執行緒之間的協作)Java程式設計執行緒
- java併發程式設計 | 執行緒詳解Java程式設計執行緒
- Java併發程式設計-執行緒基礎Java程式設計執行緒
- 併發程式設計之:執行緒池(一)程式設計執行緒
- 鴻蒙程式設計江湖:深入理解TaskPool和Worker的併發任務執行鴻蒙程式設計
- Java併發程式設計實戰總結 (一)Java程式設計
- 《JAVA併發程式設計實戰》顯式鎖Java程式設計
- Java併發程式設計實戰(4)- 死鎖Java程式設計
- Java併發程式設計實戰——讀後感Java程式設計
- Java併發程式設計實戰--筆記三Java程式設計筆記
- Java併發程式設計實戰--筆記四Java程式設計筆記
- Java併發程式設計實戰--筆記二Java程式設計筆記
- Java併發程式設計實戰--this引用逸出Java程式設計
- Java併發程式設計實戰--筆記一Java程式設計筆記