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

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

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

 import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; /**
* Created by Administrator on 2017/9/26.
*/
public class FactorialCalculator implements Callable<Integer> { private Integer number; public FactorialCalculator(Integer number) {
this.number = number;
}
public Integer call() throws Exception {
int result = 1; if (number == 0 || number == 1) {
result = 1;
}else {
for (int i = 2; i < number; i++) {
result *= i;
TimeUnit.MICROSECONDS.sleep(200);
if (i == 5) {
throw new IllegalArgumentException("excepion happend");//计算5以上的阶乘都会抛出异常. 根据需要注释该if语句
}
}
}
System.out.printf("%s: %d\n", Thread.currentThread().getName(), result);
return result;
}
}

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

二,提交任务的Main类

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

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

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

         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);
}

需要注意的是: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循环中,检查任务的状态---是否执行完成。

         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());

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

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

获取线程的执行结果

         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);
}

第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.

任务在执行过程中,若抛出异常,下面语句在获取执行结果时会直接跳到catch(ExecutionException e)中。

number = result.get()

但它不影响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

原文:http://www.cnblogs.com/hapjin/p/7599189.html

JAVA 线程池之Callable返回结果的更多相关文章

  1. Java线程池(Callable+Future模式)

    转: Java线程池(Callable+Future模式) Java线程池(Callable+Future模式) Java通过Executors提供四种线程池 1)newCachedThreadPoo ...

  2. Java线程池 / Executor / Callable / Future

    为什么需要线程池?   每次都要new一个thread,开销大,性能差:不能统一管理:功能少(没有定时执行.中断等).   使用线程池的好处是,可重用,可管理.   Executor     4种线程 ...

  3. Java线程池学习

    Java线程池学习 Executor框架简介 在Java 5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java ...

  4. java线程池的使用与详解

    java线程池的使用与详解 [转载]本文转载自两篇博文:  1.Java并发编程:线程池的使用:http://www.cnblogs.com/dolphin0520/p/3932921.html   ...

  5. java线程池分析和应用

    比较 在前面的一些文章里,我们已经讨论了手工创建和管理线程.在实际应用中我们有的时候也会经常听到线程池这个概念.在这里,我们可以先针对手工创建管理线程和通过线程池来管理做一个比较.通常,我们如果手工创 ...

  6. java 线程池 并行 执行

    https://github.com/donaldlee2008/JerryMultiThread/blob/master/src/com/jerry/threadpool/ThreadPoolTes ...

  7. Java线程池使用和分析(一)

    线程池是可以控制线程创建.释放,并通过某种策略尝试复用线程去执行任务的一种管理框架,从而实现线程资源与任务之间的一种平衡. 以下分析基于 JDK1.7 以下是本文的目录大纲: 一.线程池架构 二.Th ...

  8. JAVA线程池应用的DEMO

    在做很多高并发应用的时候,单线程的瓶颈已经满足不了我们的需求,此时使用多线程来提高处理速度已经是比较常规的方案了.在使用多线程的时候,我们可以使用线程池来管理我们的线程,至于使用线程池的优点就不多说了 ...

  9. Java线程池详解

    一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...

随机推荐

  1. SSM 即所谓的 Spring MVC + Spring + MyBatis 整合开发。

    SSM 即所谓的 Spring MVC + Spring + MyBatis 整合开发.是目前企业开发比较流行的架构.代替了之前的SSH(Struts + Spring + Hibernate) 计划 ...

  2. 自学Python4.9-生成器举例

    自学Python之路-Python基础+模块+面向对象自学Python之路-Python网络编程自学Python之路-Python并发编程+数据库+前端自学Python之路-django 自学Pyth ...

  3. 「SCOI2015」小凸想跑步 解题报告

    「SCOI2015」小凸想跑步 最开始以为和多边形的重心有关,后来发现多边形的重心没啥好玩的性质 实际上你把面积小于的不等式列出来,发现是一次的,那么就可以半平面交了 Code: #include & ...

  4. String Reconstruction (并查集)

    并查集维护和我这个位置的字母连续的已经被填充的字母能到达的最右边的第一个还没有填充的位置,然后把这个位置填上应该填的东西,然后把这个位置和下一个位置连接起来,如果下一个位置还没有填,我就会把下一个位置 ...

  5. HDU5985 Lucky Coins 概率dp

    题意:给你N种硬币,每种硬币有Si个,有Pi 概率朝上,每次抛所有硬币抛起,所有反面的拿掉,问每种硬币成为最后的lucky硬币的概率. 题解:都知道是概率dp,但是模拟赛时思路非常模糊,很纠结,dp[ ...

  6. 分考场(无向图着色问题)(dfs回溯)

    问题描述 n个人参加某项特殊考试. 为了公平,要求任何两个认识的人不能分在同一个考场. 求是少需要分几个考场才能满足条件. 输入格式 第一行,一个整数n(1<n<100),表示参加考试的人 ...

  7. OO第一阶段纪实

    $ 0 写在前面 在DDL一次次的推动下,历经三个周期的更迭,一个月的时光匆匆而过.谨撰此博文,以记录这一段见证成长的心路历程. $ 0-0 JAVA“一天速成”没有修习过传说中的“OO先导课”,在学 ...

  8. [NOI2018]归程

    今年D1T1,平心而论,如果能想到kruskal重构树还是很简单的. ......苟屁啊!虽然跟其他的比是简单些,但是思维难度中上,代码难度中上,怎么看都很符合NOI T1啊. 本题还有可持久化并查集 ...

  9. 神经网络中w,b参数的作用(为何需要偏置b的解释)

    http://blog.csdn.net/xwd18280820053/article/details/70681750 可视图讲解神经元w,b参数的作用 在我们接触神经网络过程中,很容易看到就是这样 ...

  10. Minieye杯第十五届华中科技大学程序设计邀请赛网络赛 部分题目

    链接:https://pan.baidu.com/s/12gSzPHEgSNbT5Dl2QqDNpA 提取码:fw39 复制这段内容后打开百度网盘手机App,操作更方便哦 D    Grid #inc ...