大多數并發程序都是圍繞任務進行管理的.任務就是抽象和離散的工作單元.
任務的執行策略
1.順序的執行任務
這種策略的特點是一般只有按順序處理到來的任務.一次只能處理一個任務,后來其它任務都要等待處理.響應性很糟糕,吞吐量低.系統資源利用率低.
2.顯示的為任務創建線程
為每個任務創建對應一個線程,響應快,系統資源利用路高.缺點是資源消耗量大,如果有大量任務要執行的話,系統遲早會因為無限制創建過多的線程而造成內存耗盡.特別當創建的線程數量遠遠大于系統的CPU核數,由于每一個核同一時刻只能執行一個線程,所以系統要執行很多不必要的線程上下文切換,造成資源大量浪費.
3.Executor框架
Executor接口本身很簡單,就一個execute方法.但是由Executor這個接口衍生出來的類,功能非常強大.可以這么認為,Executor框架這是線程管理的工具.可以對線程的生命周期和執行策略進行管理.
Executor接口
public interface Executor { void execute(Runnable command);}Executor框架是靠ThreadPoolExecutor實現的,簡單理解為是一個線程池.其實是通過線程池和一個阻塞隊列BlockingQueue<Runnable>對線程進行管理.
頁面渲染器實例
該實例要實現2個任務,第一是渲染文本(速度快),第二個是渲染圖片(速度慢).渲染圖片的時候要先下載圖片才能渲染.
1.第一種方式:順序執行頁面渲染
public class SingleThreadRenderer { public void renderPage(CharSequence source) { renderText(source);// 處理文本,速度快 List<ImageData> imageData = new ArrayList<>(); for (ImageInfo info : scanForImageInfo(source)) { imageData.add(info.downloadImage());// 下載圖片,速度慢 } for (ImageData data : imageData) { renderImage(data);// 處理圖片 } }}這種實現方式簡單,但是缺點也很明顯,就是渲染文本和渲染圖片不能并發執行,CPU利用率低.
2.第二種方式:使用Future實現頁面渲染器
Future可以持有異步并發線程的執行結果,Executors可以對線程執行并發操作.
public class FutureRenderer { PRivate final ExecutorService exec = Executors.newFixedThreadPool(Runtime .getRuntime().availableProcessors()); public void renderPage(CharSequence source) { final List<ImageInfo> imageInfos = scanForImageInfo(source); Callable<List<ImageData>> task = new Callable<List<ImageData>>() { public List<ImageData> call() throws Exception { List<ImageData> imageData = new ArrayList<>(); for (ImageInfo info : imageInfos) { imageData.add(info.downloadImage());// 下載圖片,速度慢 } return imageData; } }; Future<List<ImageData>> f = exec.submit(task); //渲染圖片的線程正在執行的同時處理文本任務 renderText(source);// 處理文本,速度快 try { List<ImageData> imageDatas = f.get(); for (ImageData data : imageDatas) { renderImage(data);// 處理圖片 } } catch (InterruptedException | ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}這種執行策略仍舊有局限性,這是由于并行運行異類任務并不會獲得好的性能.只有大量相互獨立的且同類的任務進行并發處理,才能獲得真正性能提升.
3.第三種方式:使用CompletionService的頁面渲染器
public class CompletionServiceRenderer { private final ExecutorService exec = Executors.newFixedThreadPool(Runtime .getRuntime().availableProcessors()); public void renderPage(CharSequence source) { final List<ImageInfo> imageInfos = scanForImageInfo(source); CompletionService<ImageData> completionService = new ExecutorCompletionService<>( exec); for (final ImageInfo info : imageInfos) { Callable<ImageData> task = new Callable<ImageData>() { public ImageData call() throws Exception { return info.downloadImage(); } }; completionService.submit(task); } renderText(source);// 處理文本,速度快 for (int i = 0; i < imageInfos.size(); i++) { try { Future<ImageData> future = completionService.take(); ImageData imageData = future.get(); renderImage(imageData);// 處理圖片 } catch (InterruptedException | ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}?這種方式不用等下載所有圖片才處理,而是每下載一張圖片就處理,實現了很好的并發行.
新聞熱點
疑難解答