一文搞懂Java异步编程之FutureTask(转)

发布时间 2023-09-28 08:32:53作者: 观山海

背景

Java异步编程的在实际开发中经常被用到,那么异步任务执行结束如何将结果通知到主线程或者其他任务呢?本文不探讨JUC包下的各类锁实现实现的任务同步或者通知。

一、Thread

狭隘的讲Java创建线程的方式只有一种,就是new Thread实例。Thread本身是Runnable的实现并且它定义了Runnable的field,所以支持自定义实现Runnable接口后,在new实例时构造Thread,最终一个新建的线程都需要通过调用start()执行。

这里有一点值得拿出来讲讲,如果直接执行Thread的run(),那么这时候并不是多线程的,它其实就是在主线程中执行了Runnable中定义的普通run方法。

由于Thread没有返回值,异步处理完的结果获取就有点麻烦。到了JDK1.5的时候,Callable和Future被引入,返回线程执行结果。

二、Future与FutureTask

讲到这里终于点题了,Future是一个接口,可以对Runnable或者Callable的task进行取消、判断是否取消、判断是否完成、获取执行结果;执行结果的获取是阻塞,直到task返回结果或者超时当前线程才会继续往下执行。

Future是一个接口,FutureTask是它的实现类,它的继承关系如下,

public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>

 

因此它可以作为Runnable被线程执行

public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<String> futureTask = new FutureTask<>(() -> {
        System.out.println(Thread.currentThread().getName()); // Thread-0
        System.out.println("Runnable implements"); // Runnable implements
    }, null);
    new Thread(futureTask).start();
    System.out.println(Thread.currentThread().getName()); // main
    String result = futureTask.get();
    System.out.println(result); // null
}

上示例代码,输出内容顺序为 main-》Thread-0-》Runnable implements-》null 。最终输出null是因为FutureTask的构造函数中传入的就是null,如果有需要可以传值给异步任务处理,通过自定义Task类实现Runnable接口,把传值作为field给到Task处理。

小结

Future可以很容易的获得异步执行的结果,并且对任务进行一些操控;get等待结果时会阻塞,所以当任务之间有依赖关系的时候,一个任务依赖另一个任务的结果,可以用Future的get来等待依赖的任务完成的结果。

FutureTask是实现类,有Runnable的特性又有Future的特性,内部包的是Callable ,当然也有接受Runnable的构造器,只是会偷偷把Runnable转成Callable来实现能返回结果的方法。