当前位置: 首页 > news >正文

JAVA 线程池之Callable返回结果

本文介绍如何向线程池提交任务,并获得任务的执行结果。然后模拟 线程池中的线程在执行任务的过程中抛出异常时,该如何处理。

 

一,执行具体任务的线程类

要想 获得 线程的执行结果,需实现Callable接口。FactorialCalculator 计算 number的阶乘,具体实现如下:

复制代码
 1 import java.util.concurrent.Callable;
 2 import java.util.concurrent.TimeUnit;
 3 
 4 /**
 5  * Created by Administrator on 2017/9/26.
 6  */
 7 public class FactorialCalculator implements Callable<Integer> {
 8 
 9     private Integer number;
10 
11     public FactorialCalculator(Integer number) {
12         this.number = number;
13     }
14     public Integer call() throws Exception {
15         int result = 1;
16 
17         if (number == 0 || number == 1) {
18             result = 1;
19         }else {
20             for (int i = 2; i < number; i++) {
21                 result *= i;
22                 TimeUnit.MICROSECONDS.sleep(200);
23                 if (i == 5) {
24                     throw new IllegalArgumentException("excepion happend");//计算5以上的阶乘都会抛出异常. 根据需要注释该if语句
25                 }
26             }
27         }
28         System.out.printf("%s: %d\n", Thread.currentThread().getName(), result);
29         return result;
30     }
31 }
复制代码

上面23行--25行的if语句表明:如果number大于5,那么 if(i==5)成立,会抛出异常。即模拟  执行5 以上的阶乘时,会抛出异常。

 

二,提交任务的Main类

下面来看看,怎样向线程池提交任务,并获取任务的返回结果。我们一共向线程池中提交了10个任务,因此创建了一个ArrayList保存每个任务的执行结果。

第一行,首先创建一个线程池。第二行,创建List保存10个线程的执行结果 所要存入的地方,每个任务是计算阶乘,因此线程的返回结果是 Integer。而这个结果只要计算出来了,是放在Future<Integer>里面。

第5-7行,随机生成一个10以内的整数,然后创建一个 FactorialCalculator对象,该对象就是待执行的任务,然后在第8行 通过线程池的submit方法提交。

复制代码
 1         ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
 2         List<Future<Integer>> resultList = new ArrayList<Future<Integer>>();
 3         Random random = new Random();
 4         for (int i = 0; i < 10; i ++) {
 5             int rand = random.nextInt(10);
 6 
 7             FactorialCalculator factorialCalculator = new FactorialCalculator(rand);
 8             Future<Integer> res = executor.submit(factorialCalculator);//异步提交, non blocking.
 9             resultList.add(res);
10         }
复制代码

需要注意的是:submit方法是个非阻塞方法,参考这篇文章。提交了任务后,由线程池里面的线程负责执行该任务,执行完成后得到的结果最终会保存在 Future<Integer>里面,正如第8行所示。

As soon as we invoke the submit() method of ExecutorService the Callable are handed over to ExecutorService to execute.
Here one thing we have to note, the submit() is not blocking.
So, all of our Callables will be submitted right away to the ExecutorService, and ExecutorService will decide when to execute which callable.
For each Callable we get a Future object to get the result later.

 

接下来,我们在do循环中,检查任务的状态---是否执行完成。

复制代码
 1         do {
 2 //            System.out.printf("number of completed tasks: %d\n", executor.getCompletedTaskCount());
 3             for (int i = 0; i < resultList.size(); i++) {
 4                 Future<Integer> result = resultList.get(i);
 5                 System.out.printf("Task %d : %s \n", i, result.isDone());
 6             }
 7             try {
 8                 TimeUnit.MILLISECONDS.sleep(50);
 9 
10             } catch (InterruptedException e) {
11                 e.printStackTrace();
12             }
13         } while (executor.getCompletedTaskCount() < resultList.size());
复制代码

第3-6行for循环,从ArrayList中取出 每个 Future<Integer>,查看它的状态 isDone() ,即:是否执行完毕。

第13行,while结束条件:当所有的线程的执行完毕时,就退出do循环。注意:当线程在执行过程中抛出异常了,也表示线程执行完毕。

 

获取线程的执行结果

复制代码
 1         System.out.println("Results as folloers:");
 2         for (int i = 0; i < resultList.size(); i++) {
 3             Future<Integer> result = resultList.get(i);
 4             Integer number = null;
 5 
 6             try {
 7                 number = result.get();// blocking method
 8             } catch (InterruptedException e) {
 9                 e.printStackTrace();
10             } catch (ExecutionException e) {
11                 e.printStackTrace();
12             }
13             System.out.printf("task: %d, result %d:\n", i, number);
14         }
复制代码

第3行取出每个存储结果的地方:Future<Integer>,第7行 从Future<Integer>中获得任务的执行结果。Future.get方法是一个阻塞方法。但前面的do-while循环里面,我们已经检查了任务的执行状态是否完成,因此这里能够很快地取出任务的执行结果。

We are invoking the get() method of Future to get the result. Here we have to remember that, the get() is a blocking method.

 

任务在执行过程中,若抛出异常,则最终Future<Integer> get() 返回 null。但它不影响Main类线程---主线程的执行。

 

整个Main类代码如下:

复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

/**
 * Created by Administrator on 2017/9/26.
 */
public class Main {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
        List<Future<Integer>> resultList = new ArrayList<Future<Integer>>();
        Random random = new Random();
        for (int i = 0; i < 10; i ++) {
            int rand = random.nextInt(10);

            FactorialCalculator factorialCalculator = new FactorialCalculator(rand);
            Future<Integer> res = executor.submit(factorialCalculator);//异步提交, non blocking.
            resultList.add(res);
        }

        // in loop check out the result is finished
        do {
//            System.out.printf("number of completed tasks: %d\n", executor.getCompletedTaskCount());
            for (int i = 0; i < resultList.size(); i++) {
                Future<Integer> result = resultList.get(i);
                System.out.printf("Task %d : %s \n", i, result.isDone());
            }
            try {
                TimeUnit.MILLISECONDS.sleep(50);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } while (executor.getCompletedTaskCount() < resultList.size());


        System.out.println("Results as folloers:");
        for (int i = 0; i < resultList.size(); i++) {
            Future<Integer> result = resultList.get(i);
            Integer number = null;

            try {
                number = result.get();// blocking method
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            System.out.printf("task: %d, result %d:\n", i, number);
        }
        executor.shutdown();
    }
}
复制代码

在上面  number = result.get(); 语句中,我们 catch了两个异常,一个是InterruptedException,另一个是ExecutionException。因为我们是在main线程内获得任务的执行结果,main线程执行 result.get()会阻塞,如果在阻塞的过程中被(其他线程)中断,则抛出InterruptedException。

若在任务的执行过程中抛出了异常(比如IllegalArgumentException),那么main线程在这里就会catch到ExecutionException。此时,就可以对抛出异常的任务“进行处理”。此外,线程池中执行该任务的线程,并不会因为 该任务在执行过程中抛出了异常而受到影响,该线程 可以继续 接收并运行 下一次提交给它的任务。

 

图中 task1、task2、task4 返回的结果为空,表明它们抛出了IllegalArgumentException异常。

而要想对异常进行处理,可参考:Java线程池异常处理最佳实践 和 这篇文章。

 

参考资料

《Java 7 Concurrency Cookbook》 chapter 4

本文转自hapjin博客园博客,原文链接:http://www.cnblogs.com/hapjin/p/7599189.html,如需转载请自行联系原作者

相关文章:

  • 用户系列之五:用户SID查看之终结版
  • SQLite第二课 源码下载编译
  • 查看Linux服务器网卡流量小脚本shell和Python各一例
  • 6.4 xz压缩工具
  • Memcached通用类(基于enyim.com Memcached Client)
  • PS如何批量生成缩略图(方法可以通用其他重复劳动)
  • .NET开源项目介绍及资源推荐:数据持久层
  • Wireshark网络抓包(二)——过滤器
  • Qt之JSON生成与解析1
  • command for cut
  • ubuntu 11.10下载和编译Android源码
  • 【移动开发】Android应用开发者应该知道的东西
  • Android开发之旅:组件生命周期(二)
  • LAMP 全功能编译安装 for CentOS6.3笔记(更新)
  • springmvc的@RequestMapping、@PathVariable、@RequestParam
  • extract-text-webpack-plugin用法
  • HomeBrew常规使用教程
  • JavaScript DOM 10 - 滚动
  • JS 面试题总结
  • js正则,这点儿就够用了
  • mysql常用命令汇总
  • Object.assign方法不能实现深复制
  • Tornado学习笔记(1)
  • 第十八天-企业应用架构模式-基本模式
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 前端存储 - localStorage
  • 如何合理的规划jvm性能调优
  • 怎么把视频里的音乐提取出来
  • nb
  • #WEB前端(HTML属性)
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (四)模仿学习-完成后台管理页面查询
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .NET分布式缓存Memcached从入门到实战
  • .Net中wcf服务生成及调用
  • .ui文件相关
  • @javax.ws.rs Webservice注解
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • [ C++ ] STL_list 使用及其模拟实现
  • [23] 4K4D: Real-Time 4D View Synthesis at 4K Resolution
  • [APIO2015]巴厘岛的雕塑
  • [C#小技巧]如何捕捉上升沿和下降沿
  • [C++] Windows中字符串函数的种类
  • [CF703D]Mishka and Interesting sum/[BZOJ5476]位运算
  • [cocos creator]EditBox,editing-return事件,清空输入框
  • [hadoop读书笔记] 第十五章 sqoop1.4.6小实验 - 将mysq数据导入HBASE
  • [HTML]Web前端开发技术29(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页
  • [Linux] Boot分区满了的处理方法 The volume boot has only 0 bytes disk space remaining