Future详解( 三 )

5.finishCompletion()方法
//任务执行完成(正常结束和非正常结束都代表任务执行完成)会调用这个方法来唤醒所有因调用get()方法而陷入阻塞的线程 。private void finishCompletion() {// 如果条件成立,说明当前有陷入阻塞的线程for (WaitNode q; (q = waiters) != null;) {if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {for (;;) {Thread t = q.thread;if (t != null) {q.thread = null;LockSupport.unpark(t);}WaitNode next = q.next;if (next == null)break;// 执行到这里说明还有因调用get()而陷入阻塞的线程,自旋接着唤醒// 这里q.next设置为null帮助GC(垃圾回收)q.next = null; // unlink to help gcq = next;}break;}}//拓展方法done();// 将callable设置为null,方便GCcallable = null;}【3】注意事项
1)当 for 循环批量获取 Future 的结果时容易 block,get 方法调用时应使用 timeout限制
2)Future 的生命周期不能后退 。一旦完成了任务,它就永久停在了“已完成”的状态,不能从头再来
3)FutureTask 一般是结合线程池使用,然后额外采用FutureTask获取结果 。
【4】Future的局限性
从本质上说,Future表示一个异步计算的结果 。它提供了isDone()来检测计算是否已经完成,并且在计算结束后,可以通过get()方法来获取计算结果 。在异步计算中,Future确实是个非常优秀的接口 。但是,它的本身也确实存在着许多限制:
1)并发执行多任务:Future只提供了get()方法来获取结果,并且是阻塞的 。所以,除了等待你别无他法;
2)无法对多个任务进行链式调用:如果你希望在计算任务完成后执行特定动作,比如发邮件,但Future却没有提供这样的能力;
3)无法组合多个任务:如果你运行了10个任务,并期望在它们全部执行结束后执行特定动作,那么在Future中这是无能为力的;
4)没有异常处理:Future接口中没有关于异常处理的方法;
了解CompletionService接口【1】介绍
1)CompletionService 接口是一个独立的接口,并没有扩展 ExecutorService。其默认实现类是ExecutorCompletionService;
2)接口CompletionService 的功能是:以异步的方式一边执行未完成的任务,一边记录、处理已完成任务的结果 。让两件事分开执行,任务之间不会互相阻塞,可以实现先执行完的先取结果,不再依赖任务顺序了 。
3)简单来说,CompletionService 就是监视着 Executor线程池执行的任务,用 BlockingQueue 将完成的任务的结果存储下来 。(当然,这个也可以是程序员自己去实现,但是要不断遍历与每个任务关联的 Future,然后不断去轮询,判断任务是否已经完成,比较繁琐);
【2】源码展示
public interface CompletionService<V> {//提交一个 Callable 任务;一旦完成,便可以由take()、poll()方法获取Future<V> submit(Callable<V> task);//提交一个 Runnable 任务,并指定计算结果;Future<V> submit(Runnable task, V result);//获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则等待 。Future<V> take() throws InterruptedException;//获取并移除表示下一个已完成任务的 Future,如果不存在这样的任务,则返回 null 。Future<V> poll();//获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则将等待指定的时间(如果有必要)Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;}了解ExecutorCompletionService类(CompletionService接口的实现类)【1】介绍
1)内部通过阻塞队列+FutureTask,实现了任务先完成可优先获取到,即结果按照完成先后顺序排序,内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,通过调用它的take方法或poll方法可以获取到一个已经执行完成的Future,进而通过调用Future接口实现类的get方法获取最终的结果 。

经验总结扩展阅读