解开Future的神秘面纱之获取结果

 前言

  在前面的两篇博文中,已经介绍利用FutureTask任务的执行流程,以及利用其实现的cancel方法取消任务的情况。本篇就来介绍下,线程任务的结果获取。

利用get方法获取程序运行结果

  我们知道利用Future接口的最重要的操作就是要获取任务的结果,而此操作对应的方法就是get。但是问题来了,如果我调用get方法的时候,任务还没有完成呢?答案就是,等它完成,当前线程将被阻塞,直到任务完成(注意,这里说的完成,指的是任务结束,因为异常而结束也算),get方法返回。主线程(不是执行任务的线程)才被唤醒,然后继续运行。

 

灵活的get方法

  有人可能会问,如果我调用get方法的时候,任务离完成还需要很长时间,那么我主线程不是会浪费一些时间?是的,如果主线程比较忙的话,这样确实主线程的效率。不过还有一个有参的get方法,此方法以等待时长为参数,如果时长结束,任务还没完成,主线程将继续执行,然后会在之后的某个时间再来获取任务结果。(当然如果主线程依赖这个任务结果才能继续执行,那么只能老老实实地等了

FutureTask的阻塞模型

  要想了解get方法的具体实现,必须先弄清楚,它是如何阻塞的。前篇博文已经提到,FutureTask有类型为WaitNode字段waiters,实际上这个waiters引用的是一个以WaitNode为节点的单向链表的头节点。如图所示:

waitNode类代码如下:

复制代码
static final class WaitNode {     volatile Thread thread; //线程    volatile WaitNode next; //下一个节点     //构造函数获取当前执行线程的引用    WaitNode() { thread = Thread.currentThread(); } }
复制代码

WaitNode保留线程引用的作用是什么?

答案是用于任务完成后唤醒等待线程。当FutureTask执行完callable的run方法后,将执行finishCompletion方法通知所有等待线程

复制代码
private void finishCompletion() {     //遍历等待节点    for (WaitNode q; (q = waiters) != null;) {         //将FutureTask的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;                 q.next = null; // unlink to help gc                q = next;             }             break;         }     }      
                        
关键字:
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信