1. 程式人生 > >Future和Callable,CompletionService實現並行化

Future和Callable,CompletionService實現並行化

Callable和Future實現並行化

Future表示一個任務的生命週期,並可以判斷是否完成和取消。 介面 方法如下:

  • boolean cancel(boolean);取消任務
  • V get();獲取結果,阻塞等待
  • V get(long, TimeUnit);最大超時等待(時間,時間單位)
  • boolean isCancelled();
  • boolean isDone(); 如下面程式碼示例:當載入頁面時,非同步並行下載圖片。實現載入文字(CPU密集型)與下載圖片(IO密集型)的並行化,不至於等待圖片的過程中,文字載入不出來:
package com.zxa.parallel;

import
com.sun.scenario.effect.ImageData; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; /** * @ClassName: parallel * @Description: //Callable和Future實現並行頁面渲染 * @Author: zhangxin_an * @CreateDate: 2018/11/7 20:05 */ public class FutureRenderer { private final ExecutorService executorService =
Executors.newCachedThreadPool(); /** * @description 載入文字與載入影象並行操作,先渲染文字,在根據載入圖形的執行緒返回值渲染影象 * @method renderPage * @params [source] * @return void * @date: 2018/11/7 20:16 * @author:zhangxin_an */ void renderPage(CharSequence source){ //獲取影象資訊(包括url) final List<ImageInfo> imageInfos =
scanForInageInfo(source); Callable<List<ImageData>> task = new Callable<List<ImageData>>() { @Override public List<ImageData> call() throws Exception { List<ImageData> result = new ArrayList<>(); for (ImageInfo imageInfo : imageInfos){ //下載圖片到本地 result.add(imageInfo.downloadImage()); } return result; } }; Future<List<ImageData>> future = executorService.submit(task); //渲染文字 renderText(source); try{ List<ImageData> imageData = future.get(); //執行渲染影象操作...TODO } catch (InterruptedException e) { Thread.currentThread().interrupt(); future.cancel(true); e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } private void renderText(CharSequence source) { } private List<ImageInfo> scanForInageInfo(CharSequence source) { return null; } }

缺點:必須所有影象載入完才渲染,併發性十分有限。 解決:保留任務關聯的每一個Future,反覆呼叫get(),同時將timeout指定為0,輪詢來判斷任務是否完成。 更好的方法:

CompletionService(完成服務)

CompletionService將Executor和BlockingQueue功能結合在一起。當將Callable任務交給CompletionService時,可以用類似於佇列的take和poll方法來獲取完成的結果,返回值為Future。ExecutorCompletionService為其實現類,並將計算結果委託給Executor. 修改如下:

package com.zxa.parallel;

import com.sun.scenario.effect.ImageData;

import java.util.List;
import java.util.concurrent.*;

/**
 * @ClassName: Renderer
 * @Description: //CompletionService實現一組任務並行獲取結果
 * @Author: zhangxin_an
 * @CreateDate: 2018/11/7 20:55
 */
public class Renderer {
	private final ExecutorService executorService;

	Renderer(ExecutorService executorService){
		this.executorService = executorService;
	}

	void renderPage(CharSequence source){
		List<ImageInfo> infos = scanForImageInfo(source);

		//建構函式傳入Executor,委託其執行任務
		CompletionService<ImageData> completionService = new ExecutorCompletionService<>(executorService);
		for(final ImageInfo imageInfo : infos){
			completionService.submit(new Callable<ImageData>() {
				@Override
				public ImageData call() throws Exception {
					return imageInfo.downloadImage();
				}
			});
		}
		renderText(source);

		try {
		for(int t = 0, n = infos.size(); t < n; t++ ){
				Future<ImageData> future = completionService.take();
				ImageData imageData = future.get();
				renderImage(imageData);
		}
	} catch (InterruptedException e) {
		e.printStackTrace();
	} catch (ExecutionException e) {
			e.printStackTrace();
		}

	}

	private void renderImage(ImageData imageData) {
	}

	private void renderText(CharSequence source) {
	}

	private List<ImageInfo> scanForImageInfo(CharSequence source) {
		return null;
	}

}


多個CompletionService可以共用一個Executor.