zoukankan      html  css  js  c++  java
  • Executors Future Callable 使用场景实例

      

    https://www.jb51.net/article/132606.htm:

    我们都知道实现多线程有2种方式,一种是继承Thread,一种是实现Runnable,但这2种方式都有一个缺陷,在任务完成后无法获取返回结果。要想获得返回结果,就得使用Callable,Callable任务可以有返回值,但是没法直接从Callable任务里获取返回值;想要获取Callabel任务的返回值,需要用到Future。所以Callable任务和Future模式,通常结合起来使用。

    试想一个场景:需要一个帖子列表接口,除了需要返回帖子列表之外,还需要返回每条帖子的点赞列表和评论列表。一页10条帖子来计算,这个接口需要访问21次数据库,访问一次数据库按100ms计算,21次,累计时间为2.1s。这个响应时间,怕是无法令人满意的。怎么办呢?异步化改造接口。

    查出帖子列表后,迭代帖子列表,在循环里起10个线程,并发去获取每条帖子的点赞列表,同时另起10个线程,并发去获取每条帖子的评论列表。这样改造之后,接口的响应时间大大缩短,在200ms。这个时候就要用Callabel结合Future来实现。

    private List<PostResponse> createPostResponseList(Page<PostResponse> page,final String userId){ 
        if(page.getCount()==0||page==null||page.getList()==null){ 
          return null; 
        } 
        //获取帖子列表 
        List<PostResponse> circleResponseList = page.getList(); 
        int size=circleResponseList.size(); 
        ExecutorService commentPool = Executors.newFixedThreadPool(size); 
        ExecutorService supportPool = Executors.newFixedThreadPool(size); 
        try { 
          List<Future> commentFutureList = new ArrayList<Future>(size); 
          if (circleResponseList != null && circleResponseList.size() > 0) { 
            for (PostResponse postResponse : circleResponseList) { 
              final String circleId=postResponse.getId(); 
              final String postUserId=postResponse.getUserId(); 
              //查评论列表 
              Callable<List<CircleReviews>> callableComment = new Callable<List<CircleReviews>>() { 
                @Override
                public List<CircleReviews> call() throws Exception { 
                  return circleReviewsBiz.getPostComments(circleId); 
                } 
              }; 
              Future f = commentPool.submit(callableComment); 
              commentFutureList.add(f); 
              //查点赞列表 
              Callable<List<CircleZan>> callableSupport = new Callable<List<CircleZan>>() { 
                @Override
                public List<CircleZan> call() throws Exception { 
                  return circleZanBiz.findList(circleId); 
                } 
              }; 
              Future supportFuture = supportPool.submit(callableSupport); 
              commentFutureList.add(supportFuture); 
            } 
      
          } 
          // 获取所有并发任务的执行结果 
          int i = 0; 
          PostResponse temp = null; 
          for (Future f : commentFutureList) { 
            temp = circleResponseList.get(i); 
            temp.setCommentList((List<CircleReviews>) f.get(); 
            temp.setSupportList((List<CircleZan>) f.get(); 
            circleResponseList.set(i, temp); 
            i++; 
          } 
      
        } catch (Exception e) { 
          e.printStackTrace(); 
        } finally { 
          // 关闭线程池 
          commentPool.shutdown(); 
          supportPool.shutdown(); 
        } 
        return circleResponseList; 
    }

    ★  下面给出一个Executor执行Callable任务的示例代码(https://blog.csdn.net/ns_code/article/details/17465497?utm_source=blogxgwz0):

    import java.util.ArrayList; 
    import java.util.List; 
    import java.util.concurrent.*; 
     
    public class CallableDemo{ 
        public static void main(String[] args){ 
            ExecutorService executorService = Executors.newCachedThreadPool(); 
            List<Future<String>> resultList = new ArrayList<Future<String>>(); 
     
            //创建10个任务并执行 
            for (int i = 0; i < 10; i++){ 
                //使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中 
                Future<String> future = executorService.submit(new TaskWithResult(i)); 
                //将任务执行结果存储到List中 
                resultList.add(future); 
            } 
     
            //遍历任务的结果 
            for (Future<String> fs : resultList){ 
                    try{ 
                        while(!fs.isDone);//Future返回如果没有完成,则一直循环等待,直到Future返回完成
                        System.out.println(fs.get());     //打印各个线程(任务)执行的结果 
                    }catch(InterruptedException e){ 
                        e.printStackTrace(); 
                    }catch(ExecutionException e){ 
                        e.printStackTrace(); 
                    }finally{ 
                        //启动一次顺序关闭,执行以前提交的任务,但不接受新任务
                        executorService.shutdown(); 
                    } 
            } 
        } 
    } 
     
     
    class TaskWithResult implements Callable<String>{ 
        private int id; 
     
        public TaskWithResult(int id){ 
            this.id = id; 
        } 
     
        /** 
         * 任务的具体过程,一旦任务传给ExecutorService的submit方法,
         * 则该方法自动在一个线程上执行
         */ 
        public String call() throws Exception {
            System.out.println("call()方法被自动调用!!!    " + Thread.currentThread().getName()); 
            //该返回结果将被Future的get方法得到
            return "call()方法被自动调用,任务返回的结果是:" + id + "    " + Thread.currentThread().getName(); 
        } 
    }

    某次执行结果如下:

       

      从结果中可以同样可以看出,submit也是首先选择空闲线程来执行任务,如果没有,才会创建新的线程来执行任务。另外,需要注意:如果Future的返回尚未完成,则get()方法会阻塞等待,直到Future完成返回,可以通过调用isDone()方法判断Future是否完成了返回。

  • 相关阅读:
    【剑指offer】数组中重复的数字
    【剑指offer】数组中只出现一次的数字
    【linux】进程存储管理
    【linux】gdb调试
    【C/C++】快速排序的两种实现思路
    【C/C++】知识点
    【计算机网络】知识点记录
    【hadoop】mapreduce原理总结
    基于社交网络的情绪化分析IV
    Android studio 升级,不用下载完整版,完美更新到2.0
  • 原文地址:https://www.cnblogs.com/hahajava/p/9807031.html
Copyright © 2011-2022 走看看