如何高效实现一个页面渲染器

一 java线程池的基础知识(磨刀不误砍柴工)
1.newFixedThreadPool。newFixedThreadPool将创建一个固定长度的线程池,每当提交一个任务时创建一个线程,直到达到线程池的最大数量,这时线程池的规模将不再变化(如果某个线程由于发生了未预期的Exception而结束,那么线程池会补充一个新的线程)。

2.newCachedThreadPool。newCachedThreadPool将创建一个可缓存的线程池,如果线程池的当前规模超过了处理需求时,那么将回收空闲的线程,当需求增加时,则可以添加新的线程,线程池的规模不存在任何限制。

3.newSingleThreadExecutor。newSingleThreadExecutor是一个单线程的Executor,它创建单个工作者线程来执行任务,如果这个线程异常结束,会创建另一个线程来替代。newSingleThreadExecutor能确保依照任务在队列中的顺序来串行执行(例如FIFO,LIFO,优先级)。

4.newScheduledThreadPool。newScheduledThreadPool创建一个固定长度的线程池,而且以延时或定时的方式执行任务,类似于Timer。
注意:Timer支持基于绝对时间而不是相对时间的调度机制,因此任务的执行对系统时钟变化很敏感,而ScheduledThreadPoolExecutor只支持基于相对时间的调度。

5.下面看一下第一种使用Future来实现页面渲染器

分析:创建一个Callable来下载所有的图像,并将其提交到一个ExecutorService。这将返回一个描述任务执行情况的Future。当主任务需要图像时,他会等待Future.get的调用结果。如果幸运的话,当开始请求时所有的图片就已经下载完成了,即使没有,至少图像的下载任务也已经提前开始了。

public abstract class FutureRenderer {
	
	private final ExecutorService executor = Executors.newCachedThreadPool();
	
	void renderPage(CharSequence source) {
		final List<ImageInfo> imageInfos = scanForImageInfo(source);
		
		Callable<List<ImageData>> task = new Callable<List<ImageData>>() {

			@Override
			public List<ImageData> call(){
				List<ImageData> result = new ArrayList<>();
				
				for(ImageInfo info:imageInfos) {
					result.add(info.downLoadImage());
				}
				return result;
			}
		
		};
		//提前提交获取图片信息的数据
		Future<List<ImageData>> future = executor.submit(task);
		//开始渲染文本
		renderText(source);
		
		try {
			List<ImageData> imageDatas = future.get();
			for(ImageData data:imageDatas) {
			 //开始渲染图片
				renderImage(data);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			Thread.currentThread().interrupt();
			
			future.cancel(true);
		} catch (ExecutionException e) {
			e.getCause();
			
		}
	} 
	
	//图片数据
	interface ImageData{
		
	}
	//图片信息
	interface ImageInfo{
		ImageData downLoadImage();
	}
	
	//渲染文本
	abstract void renderText(CharSequence s);
	//得到所需图片的信息
	abstract List<ImageInfo> scanForImageInfo(CharSequence s);
	//渲染图片
	abstract void renderImage(ImageData i);
}

缺点:用户必须等所有的图像都下载完成后,才能显示出来。对用户的体验是够有好。下面我们优化一下,做到每当下载完一幅图像就立刻显示出来。

(二)CompletionService与BlockingQueue

CompletionService将Executor和BlockingQueue的功能融合在一起。可以将Callable任意任务提交给他执行,然后使用类似于队列的操作take和poll等方法来获得以完成的结果(take和poll方法会在得出结果之前阻塞),而这些结果会在完成时将被封装为Future。

改进之后的写法如下

public abstract class Renderer {

	private final ExecutorService executor;
	
	public Renderer(ExecutorService executor) {
		this.executor = executor;
	}
	
	void renderPage(CharSequence source) {
		final List<ImageInfo> info= scanForImageInfo(source);
		
		CompletionService<ImageData> completionService = new ExecutorCompletionService<>(executor);
		
		for(final ImageInfo imageInfo:info) {
			completionService.submit(new Callable<Renderer.ImageData>() {
				
				@Override
				public ImageData call() throws Exception {
					// TODO Auto-generated method stub
					return imageInfo.downloadImage();
				}
			});
		}
		
		
		renderText(source);
		
	
		for(int t = 0,n = info.size();t < n;t++) {
			try {
				Future<ImageData> f = completionService.take();
				ImageData imageData = f.get();
				renderImage(imageData);
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt();
			}catch (ExecutionException e) {
				e.getCause();
			}
		}
		
	} 

	
	interface ImageData{
	}
	interface ImageInfo{
		ImageData downloadImage();
	}
	
	abstract void renderText(CharSequence s);
	abstract List<ImageInfo> scanForImageInfo(CharSequence s);
	abstract void renderImage(ImageData i);
	
	
}

猜你喜欢

转载自blog.csdn.net/qdh186/article/details/86152041