探索异步任务 FutureTask 的实现原理

人有逆天之时,天无绝人之路。

——《醒世恒言》

  • 也有一定面试频率。

Q:介绍下FutureTask

可被取消的异步计算。此类提供Future接口的基本实现,其中包含启动和取消计算,查询以查看计算是否完成以及检索计算结果的方法。只有在计算完成后才能检索结果;如果计算尚未完成,则get方法将阻塞。

一旦计算任务完成,就不能重新启动或取消,除非调用runAndReset(执行计算而不设置其结果,然后重置该future为初始状态,如果计算遇到异常或被取消,则操作失败。 这是专为本质上执行一次以上的任务使用而设计的)。

FutureTask可用于包装Callable或Runnable对象。由于FutureTask实现了Runnable接口,因此FutureTask可以提交给 Executor执行。

除了用作独立的类之外,此类还提供protected的方法,在创建自定义任务类时可能很有用。

Q:你刚才提到 get 方法,详细讲讲怎么就会阻塞了?

get

awaitDone

等待完成,或者在中断或超时时中止。

private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
      // 根据 timed 是否使用超时策略,获取超时期限
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
      // 自旋
    for (;;) {
          // 判断主线程是否被中断
        if (Thread.interrupted()) {
              // 是,则移除该任务并抛中断异常
            removeWaiter(q);
            throw new InterruptedException();
        }
        // 获得运行状态
        int s = state;
          // 判断运行状态是否>COMPLETING
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
              // 任务已完成,返回结果
            return s;
        }
          // 在完成中
        else if (s == COMPLETING)
              // 让步
            Thread.yield();
          // NEW 状态
        else if (q == null)
              // 说明这是第一次,创建新节点
            q = new WaitNode();
        else if (!queued)
              // 将调用者线程节点的 next 域指向 waiters
              // 然后CAS将 waiters 指向 q
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
          // 判断是否指定了超时策略
        else if (timed) {
              // 更新等待超时时间
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                  // 已超时,移除
                removeWaiter(q);
                return state;
            }
              // 按指定时间挂起线程
            LockSupport.parkNanos(this, nanos);
        }
          // 未指定超时策略
        else
              // 直接将调用者线程挂起,等待任务完成唤醒
            LockSupport.park(this);
    }
}

注意到任务被封装层等待节点安排在一个队列中等待:

WaitNode

  • 简单的链表节点来记录一个栈中等待的线程。SynchronousQueue中的栈结构有更详细的解释。
    每个节点直接保存当前执行任务的线程和 next 域。

  • waiters 就相当于栈顶指针。

    report

  • 对已完成任务返回其结果或抛出的异常。

    Q:你提到状态,具体有哪些?

    任务的运行状态。

    // volatile修饰,保证多线程下的可见性
    private volatile int state;
    private static final int NEW          = 0;
    // 任务正在完成
    private static final int COMPLETING   = 1;
    // 任务正常完成
    private static fi

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java源码模拟面试解析指南 文章被收录于专栏

<p> “挨踢”业行情日益严峻,企业招聘门槛愈来愈高,大厂hc更是少之又少,而Java技术面试普遍对基础知识的掌握考察特别深,大多数同学突击所看的 Java 面试基础知识点根本达不到面试官近乎挑剔的要求。 本专刊针对如今的校招及社招痛点,深入解析 JDK 的核心源码,探究 JDK 的设计精髓及最佳实践,同时以模拟面试的场景切入,让同学们在阅读过程中也能轻松掌握面试技巧。 本专刊购买后即可解锁所有章节,故不可以退换哦~ </p>

全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务