[笔记][Java7并发编程实战手册]4.4 在执行器中执行任务并返回结果Callable、Future

来源:互联网 发布:铁血皇城轮回数据 编辑:程序博客网 时间:2024/05/18 20:12

[笔记][Java7并发编程实战手册]系列目录


简介

  执行框架(Executor Framework)的优势之一就是,可以在运行并发任务的时候返回结果。但是需要以下两个类来实现功能:
  
1. 接口 Callable<V>

返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。

简单说:就是实现这个类,并在唯一的call() 方法里面实现自己的业务逻辑。

2.接口 Future<V>

Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。

简单说:该接口可以获取一个线程的结果。调用get的时候,如果结果没有计算完成,则阻塞。这个接口我觉得得好好理解下,在使用谷歌的AsyncHttpClient 异步的同步支持http Client的时候,里面就是使用 future来作为返回结果。利用它就能异步的请求和推送结果。

用谷歌的AsyncHttpClient简单模仿安卓的AsyncHttpClient,实现异步请求回调函数返回值
在本章将学习如何实现任务的返回结果,并在执行器中运行任务。


本章Callable和future使用心得

  1. Callable 和 Runnable类似,只不过Callable可以返回结果。
  2. Callable中的运行无论是否正常运行完成或则是抛出异常,在Future.get() 之前都不会抛出异常,并且Future.isDone() 都会返回ture
  3. ThreadPoolExecutor.getCompletedTaskCount() 是返回完成的任务数量,是指 Future.isDone() 为ture的数量。
    4.简单一句话:只要把callable任务交给执行器执行,然后坐等结果就行了,在执行一些不需要阻塞的等待运行结果的场景费用适用。

最后是一个疑问: 在谷歌的AsyncHttpClient中,AsyncHttpClient.BoundRequestBuilder builder = http.preparePost(url);Future f = builder.execute(); 代码中,不调用Future.get() 任务内容就没有被执行,也就是说在调用get() 方法之前,并没有发送http请求出去。 有关这个疑问。我猜想很有可能是 没有使用类似执行器的线程池来运行的原因吧。具体的原因等有空去看源代码才知道了。 如果有知道的可以告诉我下。


示例

场景描述: 下面示例讲的是,使用Callable任务类task,里面执行的内容是,外面传递进来什么就返回什么,其中使用随眠来模拟运算时间。然后把创建好的callable交给执行器执行。获得future对象,然后用future.get() 来获取结果

/** * Created by zhuqiang on 2015/8/30 0030. */public class Client {    public static void main(String[] args) throws ExecutionException, InterruptedException {        ArrayList<Future<Integer>>  list = new ArrayList<Future<Integer>>();        ThreadPoolExecutor ex = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);        for (int i = 0; i < 5; i++) {            Task task = new Task(i);            Future<Integer> f = ex.submit(task); //把callable的task交给线程来运行,            list.add(f);   //把future对象搜集起来。获取结果        }        ex.shutdown();        // 通过isDone来获取任务的完成状况。isDone:如果任务已完成,则返回 true。 可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。        do {            for (int i = 0; i < list.size(); i++) {                Future<Integer> f = list.get(i);                if(f.isDone()){                    System.out.printf("Main_Task_%s任务是否完成:%s\n", i,true);                }            }            TimeUnit.MILLISECONDS.sleep(500); //休眠500毫秒        }while (ex.getCompletedTaskCount() < list.size());  //当执行器中完成的任务数量 小于目标数量。则一直循环获取任务的完成情况        //所有任务都完成后,就打印出返回的结果        System.out.println("\n----------------------    Main 结果 --------------------------------\n");        for (int i = 0; i < list.size(); i++) {            Future<Integer> f = list.get(i);            System.out.printf("Main_Task_%s的结果是:%s\n", i, f.get());  //因为get() 方法如果线程还没有计算完成。则会阻塞直到返回结果。所以前面才没有直接使用get获取结果,而是获取执行器里面的任务完成状态,观察任务运行情况        }    }}class Task implements Callable<Integer>{    public int num;    public Task(int num) {        this.num = num;    }    @Override    public Integer call() throws Exception {        long  time = (long)(Math.random() * 10);        TimeUnit.SECONDS.sleep(time);//        if(1 == num){  // 其实:我想测试,在执行器中执行的时候,任务是否真的执行了,这点让我很迷茫。 因为在我做过的业务中:异步的发送http请求,使用future来发送。但是如果我不调用get() 的话,那么就没有被发送请求.然后在这里测试,没有调用get的时候的确执行了//            throw  new RuntimeException("测试抛出异常");//        }        System.out.printf("%s休眠了%s秒\n", Thread.currentThread().getName(), time);        return num;    }}

某一次的运行结果:

pool-1-thread-3休眠了3秒Main_Task_2任务是否完成:trueMain_Task_2任务是否完成:trueMain_Task_2任务是否完成:trueMain_Task_2任务是否完成:trueMain_Task_2任务是否完成:truepool-1-thread-1休眠了6秒pool-1-thread-2休眠了6秒Main_Task_0任务是否完成:trueMain_Task_1任务是否完成:trueMain_Task_2任务是否完成:truepool-1-thread-3休眠了3秒Main_Task_0任务是否完成:trueMain_Task_1任务是否完成:trueMain_Task_2任务是否完成:trueMain_Task_3任务是否完成:trueMain_Task_0任务是否完成:trueMain_Task_1任务是否完成:trueMain_Task_2任务是否完成:trueMain_Task_3任务是否完成:trueMain_Task_0任务是否完成:trueMain_Task_1任务是否完成:trueMain_Task_2任务是否完成:trueMain_Task_3任务是否完成:trueMain_Task_0任务是否完成:trueMain_Task_1任务是否完成:trueMain_Task_2任务是否完成:trueMain_Task_3任务是否完成:trueMain_Task_0任务是否完成:trueMain_Task_1任务是否完成:trueMain_Task_2任务是否完成:trueMain_Task_3任务是否完成:truepool-1-thread-1休眠了3秒----------------------    Main 结果 --------------------------------Main_Task_0的结果是:0Main_Task_1的结果是:1Main_Task_2的结果是:2Main_Task_3的结果是:3Main_Task_4的结果是:4

结果说明:
  可以看到上面的运行结果,在我们把task交给执行器执行的时候,的确是以线程的方式在运行了。等待全部任务结束后,能获取到正确的返回结果。如果我们把上面的异常打开,就能看到 就算程序抛出了异常,但是在不调用get() 之前是不会抛出异常的。然后 任务完成状态却和jdk说明一致,是true

0 0
原创粉丝点击