Java ~ Executor ~ RunnableFuture【源码】

前言


 文章

一 RunnableFuture(可运行未来)接口源码及机制详解


 类

    RunnableFuture(可运行未来)接口是Future(未来)接口的子接口,因此其同样可以代表任务,并追踪/获取代表任务的执行状态/结果。未来任务接口是对未来接口的代理性拓展,其通过继承Runnable(可运行)接口得到的run()方法将被作为代理任务的执行入口,并为代理任务执行织入代理行为提供上下文环境。如何理解“run()方法将被作为代理任务的执行入口”这句话呢?在未来接口的相关文章中我们讲过未来可视为任务的载体,因为其代表/关联着一个任务,放在代码里的具体表现为未来接口实现类会组合可运行接口变量来承接代理任务。而“run()方法将被作为代理任务的执行入口”的意思就是在可运行未来接口的run()方法中调用代理任务的run()方法以令其执行。这么做的好处在于我们将获得“切面”,即获得了可供代理任务定义代理行为的上下文环境。代理行为可实现“在代理任务执行前选择执行线程”,“在代理任务执行后保存执行结果”及“在代理任务执行后打印错误日志”等功能,为管理/追溯代理任务的流程/状态提供了实现基础,这一点在后续学习可运行未来接口实现类时会感觉尤为深刻。

    为代理行为提供上下文环境并无需指定run()方法,实现可运行未来接口的核心原因是为了令未来也成为任务。很容易就能发现的是:为代理行为提供上下文环境只需额外方法即可,并不需要强制为run()方法,从该方面来说可运行未来接口似乎没有存在必要,因为未来接口完全可以自定义方法完成该功能。事实上也确实如此,使用run()方法为代理行为提供上下文环境其实是顺势而为,实现可运行未来接口的核心原因是为了令未来也成为任务。想要理解这一点单单关注可运行未来接口是不够的,我们需要将目光延伸至Executor(执行器)API中。执行器API被专用于执行任务,而在Java中任务的外在表现即为可运行。因此无论执行器接口实现类的内部逻辑如何复杂,其最终必然要调用可运行接口定义的run()方法来执行任务。在不关注任务执行过程/结果的情况下,我们可以直接调用执行器的execute(Runnable command)方法来执行任务。但如果有相关要求,就必须调用submit(Runnable task)方法将任务封装为未来后再执行,即任务从原本“执行器 ——> 任务”式的直接执行变为了“执行器 ——> 未来 ——> 任务”式的代理执行。但无论中间过程如何,对于执行器来说任务就是任务,不会因为任务被封装为未来而有所区别,因此未来本身也必须是任务(即可运行),这样才能保证执行器调用run()方法可以兼容地执行未来(可知所有未来接口实现类都是可运行未来接口实现类),至于未来如何在run()方法中调用任务的run()方法,则由未来接口实现类自身决定。而如果未来不是任务,执行器接口实现类就必须在内部实现中进行特殊判断,并调用专用方法来执行未来。这么做在实现上并非不可行,但会将未来接口实现类的内部逻辑引入执行器接口实现类中造成二者耦合(并且两类接口各自存在多种实现类),因此是不允许的。可以发现,可运行未来接口实际上是对静态代理模式的变相运用,并对代理类提供了进一步功能性拓展,具体结构图如下。

/**
 * A {@link Future} that is {@link Runnable}. Successful execution of the {@code run} method causes completion of
 * the {@code Future} and allows access to its results.
 * 一个可运行的未来。run()方法的成功执行将导致未来的完成,并允许访问其结果。(RunnableFuture接口继承了
 * Runnable及Future接口,这意味着其不但可以代表任务,还可以用于执行。RunnableFuture(运行未来)接口被设计
 * 出来的目的就是为了定义这样一个具有双重功能的Future(未来)接口,从而将run()方法作为任务具体执行的入口。
 * 如果理解“将run()方法作为任务具体执行的入口”这句话呢?在讲述Future(未来)接口时我们说过其可以被看做是任
 * 务的载体,因为其代表/关联着一个任务,而放在代码里的具体表现就是其实现类会组合Runnable(可运行的)接口
 * 变量或Callable(可调用的)接口变量。而“将run()方法作为任务具体执行的入口”的意思就是在RunnableFuture(运
 * 行未来)接口的run()方法中调用Runnable(可运行的)接口变量的run()方法或Callable(可调用的)接口变量的call()
 * 方法。那这么做的好处在哪里呢?花了这么多功夫兜兜转转,总要有些能看得见的收益吧?事实上确实有,其好处在于
 * 可以对任务的整体执行进行调控。例如在任务执行前判断任务是否满足执行条件或执行一些自定义的前置操作;又例如
 * 在任务执行结束之后对执行结果/异常进行保存或执行一些自定义的后置操作等等,这些具有代理性质的行为都可以在
 * run()方法中进行实现,从而使得对任务的管理更加健壮,而提供的功能也相对更加丰富,关于这一点在学习
 * RunnableFuture(运行未来)接口的实现类FutureTask(未来任务)类的时候会感觉尤为深刻。)。
 *
 * @param <V> The result type returned by this Future's {@code get} method Future的get()方法返回的结果类型
 * @author Doug Lea
 * @Description: 可运行未来接口
 * @see FutureTask
 * @see Executor
 * @since 1.6
 */
public interface RunnableFuture<V> extends Runnable, Future<V> {
    ...
}

 方法

  • void run() —— 运行 —— 执行当前运行未来的代理任务及其代理行为。
        run()方法继承自可运行接口,但令人疑惑是都是接口,可运行未来接口为什么还要重写run()方法呢?实际上重写的意义就是为了当有开发者尝试自重写run()方法时,告诉其run()方法在这里的作用是设置执行结果、检查任务状态等…嗯…就是为了改下文档描述…
/**
 * Sets this Future to the result of its computation unless it has been cancelled.
 * 将此Future设置为其计算结果,除非它已被取消。
 */
@Override
void run();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

说淑人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值