• 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,使用户获得更加动态的界面;

  • 相关阅读:
    Hive2.0函数大全(中文版)
    Centos7 安装并配置redis
    Java内部类
    beeline: 新版连接Hive server的工具
    jsoup的Document类
    Jsoup类
    jsoup的Node类
    jsoup的Element类
    Java中的多线程
    Java中的文件IO流
  • 原文地址:https://www.cnblogs.com/Joey44/p/10491071.html
Copyright © 2020-2023  润新知