标签:java 并发编程 future callable 多线程
执行框架(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,实现异步请求回调函数返回值
在本章将学习如何实现任务的返回结果,并在执行器中运行任务。
最后是一个疑问: 在谷歌的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任务是否完成:true
Main_Task_2任务是否完成:true
Main_Task_2任务是否完成:true
Main_Task_2任务是否完成:true
Main_Task_2任务是否完成:true
pool-1-thread-1休眠了6秒
pool-1-thread-2休眠了6秒
Main_Task_0任务是否完成:true
Main_Task_1任务是否完成:true
Main_Task_2任务是否完成:true
pool-1-thread-3休眠了3秒
Main_Task_0任务是否完成:true
Main_Task_1任务是否完成:true
Main_Task_2任务是否完成:true
Main_Task_3任务是否完成:true
Main_Task_0任务是否完成:true
Main_Task_1任务是否完成:true
Main_Task_2任务是否完成:true
Main_Task_3任务是否完成:true
Main_Task_0任务是否完成:true
Main_Task_1任务是否完成:true
Main_Task_2任务是否完成:true
Main_Task_3任务是否完成:true
Main_Task_0任务是否完成:true
Main_Task_1任务是否完成:true
Main_Task_2任务是否完成:true
Main_Task_3任务是否完成:true
Main_Task_0任务是否完成:true
Main_Task_1任务是否完成:true
Main_Task_2任务是否完成:true
Main_Task_3任务是否完成:true
pool-1-thread-1休眠了3秒
---------------------- Main 结果 --------------------------------
Main_Task_0的结果是:0
Main_Task_1的结果是:1
Main_Task_2的结果是:2
Main_Task_3的结果是:3
Main_Task_4的结果是:4
结果说明:
可以看到上面的运行结果,在我们把task交给执行器执行的时候,的确是以线程的方式在运行了。等待全部任务结束后,能获取到正确的返回结果。如果我们把上面的异常打开,就能看到 就算程序抛出了异常,但是在不调用get() 之前是不会抛出异常的。然后 任务完成状态却和jdk说明一致,是true
版权声明:本文为博主原创文章,未经博主允许不得转载。
[笔记][Java7并发编程实战手册]4.4 在执行器中执行任务并返回结果Callable、Future
标签:java 并发编程 future callable 多线程
原文地址:http://blog.csdn.net/mr_zhuqiang/article/details/48108577