通过多次优化实例来了解选择并发策略的正确姿势
通过模拟浏览器程序的渲染页面(Page-Rendering)功能,为了方便,假设HTML页面只会包含标签文本和图片以及URL;
第一个版本:串行加载页面元素
public classSingleThreadRenderer{voidrenderPage(CharSequence source){
renderText(Source);
List imageData = new ArrayList();for(ImageInfo imageInfo : scanForImageInfo(source))
imageData.add(imageInfo.downloadImage());for(ImageData data : ImageData)
renderImage(data);
}
}
存在的问题:浏览器加载图片之前需要下载图片,此时如果存在网络拥塞,那么此时的CPU几乎没怎么用,大都在等待I/O操作执行完成,也会使用户体验降低:图片没下载完,文字就加载不出来;
改进版本1:使用Future实现页面渲染
/***@authorYHW
* @ClassName: FutureRenderer
* @Description:
* @date 2019/3/28 16:21*/
public classFutureRenderer {privateExecutorService executor ;voidrenderPage(CharSequence source){final List imageInfos =scanForImageInfo(source);
Callable> task = new Callable>(){public Listcall(){
List result = new ArrayList();for(ImageInfo imageInfo : imageInfos)
result.add(imageInfo.downloadImage());returnresult;
}
};
Future> future =executor.submit(task);
renderText(source);try{
List imageData =future.get();for(ImageData data : imageData){
renderImage(data);
}
}catch(InterruptedException e){
Thread.currentThread().interrupt();
future.cancel(true);
}catch(ExecutionException e){throwlaunderThrowable(e.getCause());
}
}
}
该版本使得页面文本和图片实现异步加载,但还有可以优化的地方,假设渲染文本的速度远大于图片的下载速度(很有可能),那么该版本与串行程序最后的性能差别不大,所以此改进方法对于性能的提升非常有限,而代码却更加复杂,其实在大量相互独立且同构的任务可以并发进行处理时,才能体现出将程序的负载分配带来真正的性能提升;
改进版本2:使用完成服务(CompletionService),其基于Executor和BlockingQueue,可以将Callable任务交给它来执行,再使用类似队列的出队操作来获取结果:
public classRenderer {private finalExecutorService executor;
Renderer(ExecutorService executor){this.executor =executor; }voidrevderPage(Charquence source){
List info =scanForImageInfo(source);
CompletionService completionService = new ExecutorComplementService(executor);for(finalImageInfo imageInfo : info)
completionService.submit(new Callable(){publicImageData call(){returnimageInfo.downloadImage();
}
});
renderText(source);try{for(int t = 0, n = info.size(); t < n; t++){
Future f =completionService.take():
ImageData imageData=f.get();
renderImage(imageData);
}
}catch(InterruptedException e){
Thread。currentThread().interrupt();
}catch(ExecutionException e){throwlaunderThrowbale(e.getCause());
}
}
}
经过第二次的改进,页面更加“响应式”,每个图片都会在下载完成后直接加载渲染至页面,同时异步加载HTML中的文本和URL,使用户获得更加动态的界面;