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.