FutureTask原理解析

引言

我们都知道Runable接口,它非常简单。但是它有一个问题——无法获取执行结果,以及一旦可以获取执行结果,什么时候可以获取执行结果?

public interface Runnable {
   
    public abstract void run();
}

FutureTask就是为了解决这个问题的,将Runnable包装为FutureTask之后,就可以get获取任务执行结果,如果任务没有执行完,那么当前线程就会阻塞。
FutureTask确实非常好用,但我一直以来都比较好奇,将Runnable包装为FutureTask,为何就能实现执行结果的自动获取?或者换句话说,FutureTask的原理究竟是什么?

概述

我们先来概述一下整体的结构以及整体的设计。
请添加图片描述
FutureTask实现了RunnaleFuture接口,而后者则是继承自两个接口Future和Runnable,相当于两个接口的混合(接口支持多继承)。
事实上,Future接口也就定义了FutureTask类特性的交互协议。同样,它也非常简单。那么如何实现阻塞等待执行结果呢?

public interface Future<V> {
   

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

考虑到多线程的环境——可能会有多个线程同时获取结果,一起阻塞,所以很自然地,我们需要将这些线程阻塞等待在一个列表之上,当任务执行完成之后,唤醒这些线程获取结果。
这也是FutureTask整体的设计思想,接下来,我们通过源码解析,探究它如何通过CAS的方式实现任务阻塞等待,以及维护等待队列。

源码解析

回调

Runnable是不返回结果的,所以首先,他会将Runnalbe执行和它关联的执行结果Result包装为带返回结果的Callable。这些都非常简单。

public interface Callable<V> {
   
    V call() throws Exception;
}
static final class RunnableAdapter<T> implements Callable<T> {
   
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
   
        this.task = task;
        this.result = result;
    }
    public T call() {
   
        task.run();
        return result;
    }
}
// FutureTask构造函数,将二者包装为callable
public FutureTask(Runnable runnable, V result) {
   
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

状态

FutureTask中设置了多种状态变量,用于标志任务执行的状态。理解源代码,首先需要理解这些状态的变化协议。

state有四种可能的状态转换:

  1. NEW -> COMPLETING -> NORMAL
  2. NEW -> COMPLETING -> EXCEPTIONAL
  3. NEW -> CANCELLED
  4. NEW -> INTERRUPTING -> INTERRUPTED

其中

  • NEW为初始状态,任务执行过程也为该状态。
  • COMPLETING 为中间状态,表示任务已经执行完,有线程正在调用set设置结果
  • INTERRUPTING 为中间状态,表示正在中断中
  • NORMAL,EXCEPTIONAL,CANCELLED,INTERRUPTED则为终结状态,分别表示正常结束,异常结束,任务取消,被中断

源码注释

有了上述知识,FutureTask的源码应该算比较简单,这里提供了核心代码的注释,从以下两个方面切入即可:

  • run,任务执行,执行完调用set,set会调用finishCompletion唤醒所有等待线程
  • get,获取结果,如果没有完成则阻塞当前线程插入队列

其次,FutureTask广泛使用了CAS,例如:

UNSAFE.compareAndSwapObject(this, runnerOffset
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值