Java并发(六):并发策略

通过多次优化实例来了解选择并发策略的正确姿势

  通过模拟浏览器程序的渲染页面(Page-Rendering)功能,为了方便,假设HTML页面只会包含标签文本和图片以及URL;

  

  第一个版本:串行加载页面元素

public class SingleThreadRenderer{
    void renderPage(CharSequence source){
        renderText(Source);
        List<ImageData> imageData = new ArrayList<ImageData>();
        for(ImageInfo imageInfo : scanForImageInfo(source))
            imageData.add(imageInfo.downloadImage());
        for(ImageData data : ImageData)
            renderImage(data);
    }
}

  存在的问题:浏览器加载图片之前需要下载图片,此时如果存在网络拥塞,那么此时的CPU几乎没怎么用,大都在等待I/O操作执行完成,也会使用户体验降低:图片没下载完,文字就加载不出来;

 

  改进版本1:使用Future实现页面渲染

/**
 * @author YHW
 * @ClassName: FutureRenderer
 * @Description:
 * @date 2019/3/28 16:21
 */
public class FutureRenderer {
    private ExecutorService executor ;

    void renderPage(CharSequence source){
        final List<ImageInfo> imageInfos = scanForImageInfo(source);
        Callable<list<ImageData>> task = new Callable<list<ImageData>>(){
            public List<ImageData> call(){
                List<ImageData> result = new ArrayList<ImageData>();
                for(ImageInfo imageInfo : imageInfos)
                    result.add(imageInfo.downloadImage());
                return result;
            }
        };

        Future<List<ImageData>> future = executor.submit(task);
        renderText(source);

        try{
            List<ImageData> imageData = future.get();
            for(ImageData data : imageData){
                renderImage(data);
            }
        }catch(InterruptedException e){
            Thread.currentThread().interrupt();
            future.cancel(true);
        }catch(ExecutionException e){
            throw launderThrowable(e.getCause());
        }
    }
}

  该版本使得页面文本和图片实现异步加载,但还有可以优化的地方,假设渲染文本的速度远大于图片的下载速度(很有可能),那么该版本与串行程序最后的性能差别不大,所以此改进方法对于性能的提升非常有限,而代码却更加复杂,其实在大量相互独立且同构的任务可以并发进行处理时,才能体现出将程序的负载分配带来真正的性能提升;

 

  改进版本2:使用完成服务(CompletionService),其基于Executor和BlockingQueue,可以将Callable任务交给它来执行,再使用类似队列的出队操作来获取结果:

public class Renderer {
    private final ExecutorService executor;
    
    Renderer(ExecutorService executor){ this.executor = executor; }

    void revderPage(Charquence source){
        List<ImageInfo> info = scanForImageInfo(source);
        CompletionService<ImageData> completionService = new ExecutorComplementService<ImageData>(executor);
        for(final ImageInfo imageInfo : info)
            completionService.submit(new Callable<ImageData>(){
                public ImageData call(){
                    return imageInfo.downloadImage();
                }
            });
        
        renderText(source);

        try{
            for(int t = 0, n = info.size(); t < n; t++){
                Future<ImageData> f = completionService.take():
                ImageData imageData = f.get();
                renderImage(imageData);
            }
        }catch(InterruptedException e){
            Thread。currentThread().interrupt();
        }catch(ExecutionException e){
            throw launderThrowbale(e.getCause());
        }
    }
}

 

  经过第二次的改进,页面更加“响应式”,每个图片都会在下载完成后直接加载渲染至页面,同时异步加载HTML中的文本和URL,使用户获得更加动态的界面;

 

posted @ 2019-03-29 09:56  Joey44  阅读(456)  评论(0编辑  收藏  举报
BACK TO TOP