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

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

时间: 2024-08-10 19:13:44

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

Java线程池,获取返回值

程序中使用的是Callable接口,可以获取线程的返回值. package liuzh; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.Lin

Java线程池(Callable+Future模式)

Java通过Executors提供四种线程池 1)newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. 2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待. 3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行. 4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行

java线程池的自带监控,线程池返回值的控制

当使用java线程池的时候,返回值类型用future<T> 来接收,比如: Future<String> future = fixedThreadPool.submit(new Thread());返回的结果必须由线程执行完毕后才会返回,我们可以利用这一点进行线程堵塞,监控线程的结束时间. package com.test; import java.util.ArrayList; import java.util.List; import java.util.concurrent.

java线程池分析和应用

比较 在前面的一些文章里,我们已经讨论了手工创建和管理线程.在实际应用中我们有的时候也会经常听到线程池这个概念.在这里,我们可以先针对手工创建管理线程和通过线程池来管理做一个比较.通常,我们如果手工创建线程,需要定义线程执行对象,它实现的接口.然后再创建一个线程对象,将我们定义好的对象执行部分装载到线程中.对于线程的创建.结束和结果的获取都需要我们来考虑.如果我们需要用到很多的线程时,对线程的管理就会变得比较困难.我们手工定义线程的方式在时间和空间效率方面会存在着一些不足.比如说我们定义好的线程

JAVA线程池应用的DEMO

在做很多高并发应用的时候,单线程的瓶颈已经满足不了我们的需求,此时使用多线程来提高处理速度已经是比较常规的方案了.在使用多线程的时候,我们可以使用线程池来管理我们的线程,至于使用线程池的优点就不多说了. Java线程池说起来也简单,简单说下继承关系: ThreadPoolExecutor extends AbstractExecutorService implements ExecutorService extends Executor 还有一个支持延时执行线程和可以重复执行线程的实现类: Sc

Java线程池介绍

Java线程池介绍 2015-10-24 ImportNew (点击上方公号,可快速关注) 原文:allegro 译文:ImportNew - paddx 链接:http://www.importnew.com/16845.html 根据摩尔定律(Moore’s law),集成电路晶体管的数量差不多每两年就会翻一倍.但是晶体管数量指数级的增长不一定会导致 CPU 性能的指数级增长.处理器制造商花了很多年来提高时钟频率和指令并行.在新一代的处理器上,单线程程序的执行速率确实有所提高.但是,时钟频率

Java线程池与java.util.concurrent

Java(Android)线程池 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start(); 那你就out太多了,new Thre

Java 线程池分析

在项目中经常会用到java线程池,但是别人问起线程池的原理,线程池的策略怎么实现的? 答得不太好,所以按照源码分析一番,首先看下最常用的线程池代码: public class ThreadPoolTest { private static Executor executor= Executors.newFixedThreadPool(10); //一般通过fixThreadPool起线程池 public static void main(String[] args){ for(int i=0;i

我眼中的java线程池实现原理

最近在看java线程池实现方面的源码,在此做个小结,因为网上关于线程池源码分析的博客挺多的,我也不打算重复造轮子啦,仅仅用纯语言描述的方式做做总结啦! 个人认为要想理解清楚java线程池实现原理,明白下面几个问题就可以了: (1):线程池存在哪些状态,这些状态之间是如何进行切换的呢? (2):线程池的种类有哪些? (3):创建线程池需要哪些参数,这些参数的具体含义是什么? (4):将任务添加到线程池之后运行流程? (5):线程池是怎么做到重用线程的呢? (6):线程池的关闭 首先回答第一个问题: