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来实现。

  1. private List<PostResponse> createPostResponseList(Page<PostResponse> page,final String userId){
  2. if(page.getCount()==0||page==null||page.getList()==null){
  3. return null;
  4. }
  5. //获取帖子列表
  6. List<PostResponse> circleResponseList = page.getList();
  7. int size=circleResponseList.size();
  8. ExecutorService commentPool = Executors.newFixedThreadPool(size);
  9. ExecutorService supportPool = Executors.newFixedThreadPool(size);
  10. try {
  11. List<Future> commentFutureList = new ArrayList<Future>(size);
  12. if (circleResponseList != null && circleResponseList.size() > 0) {
  13. for (PostResponse postResponse : circleResponseList) {
  14. final String circleId=postResponse.getId();
  15. final String postUserId=postResponse.getUserId();
  16. //查评论列表
  17. Callable<List<CircleReviews>> callableComment = new Callable<List<CircleReviews>>() {
  18. @Override
  19. public List<CircleReviews> call() throws Exception {
  20. return circleReviewsBiz.getPostComments(circleId);
  21. }
  22. };
  23. Future f = commentPool.submit(callableComment);
  24. commentFutureList.add(f);
  25. //查点赞列表
  26. Callable<List<CircleZan>> callableSupport = new Callable<List<CircleZan>>() {
  27. @Override
  28. public List<CircleZan> call() throws Exception {
  29. return circleZanBiz.findList(circleId);
  30. }
  31. };
  32. Future supportFuture = supportPool.submit(callableSupport);
  33. commentFutureList.add(supportFuture);
  34. }
  35.  
  36. }
  37. // 获取所有并发任务的执行结果
  38. int i = 0;
  39. PostResponse temp = null;
  40. for (Future f : commentFutureList) {
  41. temp = circleResponseList.get(i);
  42. temp.setCommentList((List<CircleReviews>) f.get();
  43. temp.setSupportList((List<CircleZan>) f.get();
  44. circleResponseList.set(i, temp);
  45. i++;
  46. }
  47.  
  48. } catch (Exception e) {
  49. e.printStackTrace();
  50. } finally {
  51. // 关闭线程池
  52. commentPool.shutdown();
  53. supportPool.shutdown();
  54. }
  55. return circleResponseList;
  56. }

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

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.concurrent.*;
  4.  
  5. public class CallableDemo{
  6. public static void main(String[] args){
  7. ExecutorService executorService = Executors.newCachedThreadPool();
  8. List<Future<String>> resultList = new ArrayList<Future<String>>();
  9.  
  10. //创建10个任务并执行
  11. for (int i = 0; i < 10; i++){
  12. //使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
  13. Future<String> future = executorService.submit(new TaskWithResult(i));
  14. //将任务执行结果存储到List中
  15. resultList.add(future);
  16. }
  17.  
  18. //遍历任务的结果
  19. for (Future<String> fs : resultList){
  20. try{
  21. while(!fs.isDone);//Future返回如果没有完成,则一直循环等待,直到Future返回完成
  22. System.out.println(fs.get()); //打印各个线程(任务)执行的结果
  23. }catch(InterruptedException e){
  24. e.printStackTrace();
  25. }catch(ExecutionException e){
  26. e.printStackTrace();
  27. }finally{
  28. //启动一次顺序关闭,执行以前提交的任务,但不接受新任务
  29. executorService.shutdown();
  30. }
  31. }
  32. }
  33. }
  34.  
  35. class TaskWithResult implements Callable<String>{
  36. private int id;
  37.  
  38. public TaskWithResult(int id){
  39. this.id = id;
  40. }
  41.  
  42. /**
  43. * 任务的具体过程,一旦任务传给ExecutorService的submit方法,
  44. * 则该方法自动在一个线程上执行
  45. */
  46. public String call() throws Exception {
  47. System.out.println("call()方法被自动调用!!! " + Thread.currentThread().getName());
  48. //该返回结果将被Future的get方法得到
  49. return "call()方法被自动调用,任务返回的结果是:" + id + " " + Thread.currentThread().getName();
  50. }
  51. }

某次执行结果如下:

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

Executors Future Callable 使用场景实例的更多相关文章

  1. Java多线程之Executor、ExecutorService、Executors、Callable、Future与FutureTask

    1. 引子 初学Java多线程,常使用Thread与Runnable创建.启动线程.如下例: Thread t1 = new Thread(new Runnable() { @Override pub ...

  2. JVM源码分析-类加载场景实例分析

    A类调用B类的静态方法,除了加载B类,但是B类的一个未被调用的方法间接使用到的C类却也被加载了,这个有意思的场景来自一个提问:方法中使用的类型为何在未调用时尝试加载?. 场景如下: public cl ...

  3. Linux 下 svn 场景实例及常用命令详解

    一.SVN使用场景实例 问题: 在使用svn做为版本控制系统的软件开发中,经常会有这样的需求:在工作复本目录树的不同目录中增加了很多文件,但未纳入版本控制系统,这时如果使用svn add命令一个一个的 ...

  4. Java并发编程 - Executor,Executors,ExecutorService, CompletionServie,Future,Callable

    一.Exectuor框架简介 Java从1.5版本开始,为简化多线程并发编程,引入全新的并发编程包:java.util.concurrent及其并发编程框架(Executor框架). Executor ...

  5. Java 多线程并发 Future+callable 实例

    需求:一个业务实现 查询, 因为 要查询十几次, 所以每个平均0.6秒, 之前只有主线程一步步查 ,结果用了10秒,效率十分低下 , 于是改用线程池并发: 以下是代码设计: 1.线程池工具类: pac ...

  6. java 多线程 Future callable

    面向对象5大设计原则 1.单一职责原则  一个类只包含它相关的方法,增删改查.一个方法只包含单一的功能,增加.一个类最多包含10个方法,一个方法最多50行,一个类最多500行.重复的代码进行封装,Do ...

  7. 并发编程-Future+callable+FutureTask 闭锁机制

    项目中经常有些任务需要异步(提交到线程池中)去执行,而主线程往往需要知道异步执行产生的结果,这时我们要怎么做呢?用runnable是无法实现的,我们需要用callable实现. FutureTask ...

  8. 多线程常用代码 Future Callable Runable

    public class ThreadPoolTest { public static void main(String[] args) throws InterruptedException { E ...

  9. Future Callable 线程池 例1

    package com.niewj.concurrent; import java.util.concurrent.Callable; import java.util.concurrent.Exec ...

随机推荐

  1. 3-23 Rspec自动化测试(开始练习)

    闰年程序 leap_year_spec.rb require_relative './leap_year' describe "Leap Year" do it "201 ...

  2. 3-8《Ruby元编程》第二章对象模型

    <Ruby元编程> 第二章 对象模型 类定义揭秘inside class definitions: class关键字更像一个作用域操作符,核心作用是可以在里面随时定义方法. [].meth ...

  3. UVA-1629 Cake slicing (DP、记忆化搜索)

    题目大意:一块n*m的矩形蛋糕,有k个草莓,现在要将蛋糕切开使每块蛋糕上都恰有一个(这意味着不能切出不含草莓的蛋糕块)草莓,要求只能水平切或竖直切,求最短的刀切长度. 题目分析:定义状态dp(xa,y ...

  4. 在oaf中集成SpringLoaded实现热部署

    首先声明:其实JRebel和Spring-Loaded就是一个开发环境下的利器,skip build and redeploy process,大大提升了工作效率!而非生产环境的利器... 不要在生产 ...

  5. java标号

    标号用于控制循环执行流程: public static void main(String[] args) { mark: for(int i = 0; i < 3; i++) { System. ...

  6. JavaScript学习总结(十八)——JavaScript获取浏览器类型与版本

    从网上找到一段使用JavaScript判断浏览器以及浏览器版本的比较好的代码,在此记录一下: 1 <script type="text/javascript"> 2 v ...

  7. Rotate Array 旋转数组 JS 版本解法

    Given an array, rotate the array to the right by k steps, where k is non-negative. 给定一个数组,并且给定一个非负数的 ...

  8. operator diag-up setting

  9. 3.strcpy使用注意(3)

    void test3(char * str1) { if(str1==NULL) { return; } char string[10]; if(strlen(str1)<=10) { strc ...

  10. POJ 2195 Going Home 最小费用流 难度:1

    Going Home Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 17955   Accepted: 9145 Descr ...