Java 异步编程之 FutureTask

更多并发相关内容,查看==>Java 线程&并发学习目录

目录
Java 异步编程之 FutureTask
1、自定义Demo 获取线程执行结果
2、Callable 接口
3、Future 接口
4、FutureTask 类
4.1、FutureTask 部分源码说明

1、自定义Demo 获取线程执行结果

在Java多线程编程中,创建线程执行一般有两种方法

  • 实现Runnable接口,创建Task然后提交给线程执行
  • 继承Thread类,创建新线程执行

但是这两种方法都有个同样的问题,无法获得线程执行的结果,run方法都是返回void

其实也可以有方法去解决,再包装一层run方法,使得获取到执行结果,再通过另一个方法去获取该结果,例如如下的样例代码

Java 异步编程之 FutureTask_第1张图片
image

Java 异步编程之 FutureTask_第2张图片
image

Java 异步编程之 FutureTask_第3张图片
image

最后输出的内容


image

从结果上可以看出来,确实是利用了创建的新线程计算出了当前的时间戳,符合我们最初级的要求

原理也非常的简单,只是重新包装了一下Runnable接口,在其void run() 方法中计算出期望的结果,然后存储到data字段,最后在主线程的方法中调用resutl.getData方法获取线程的结果,虽然这个实现是非常低级的,但是也已经说明了一些问题,我们想要实现从线程中获取计算结果,必须侵入到void run() 方法中,然后进行改造存储计算结果,提供其他的方法供使用方取得计算结果,在Java5中添加的Callable 接口就是为了实现该功能,接下来

2、Callable 接口

Java 异步编程之 FutureTask_第4张图片
image

Callable 接口只有一个方法,V call() 提供给外界使用,其实和上文中自定义的Result接口功能接近,正如该接口的文档描述所说,获取任务执行的结果,运行中可能会抛出异常,和Runnable接口功能不同

一般不会单独使用这个Callable接口,更多的是在异步的获取线程结果,或者回调结果等

3、Future 接口

public interface Future {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

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

在Java5中新增的接口,主要用途同样是在线程操作中,例如某线程得长时间等待才能得出计算结果,但是又不必须立马就使用,就可以使用该接口,把线程的计算结果赋值给Future对象中,然后等到使用的时候通过get方法就可以轻松的获取计算结果,继续操作,避免长时间的等待,可以提高效率

4、FutureTask 类

上面的demo肯定有关注到主方法中存在一个睡眠1s的操作,其实是为了确保下面的resutl.getData方法一定可以获取到值。这主要是没有Future类的get方法的功能,当没计算出结果时,就会长时间等待轮询,直到结果出现,
这点也恰好说明了Future是异步处理,同步等待结果,切记勿随便在主线程这样操作,避免夯住主线程,造成服务不可用的情况。

FutureTask就是吸收了Future的特点,再组合Callable的能力,从而实现获取线程结果,并且可以利用线程中断随时终止线程的执行,如下FutureTask类图

Java 异步编程之 FutureTask_第5张图片
image
public class FutureTask implements RunnableFuture {
  
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

    private Callable callable;
    private Object outcome; 
    private volatile Thread runner;
    private volatile WaitNode waiters;

    public FutureTask(Callable callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
    ....

如上FutureTask部分代码,就是传入一个callback对象,或者已经包含结果的Runnable对象,因为FutureTask本身也是继承了Runnable接口,所以可以直接当做一个Task丢给线程、线程池去运行的,看看FutureTask具体的样例代码


Java 异步编程之 FutureTask_第6张图片
image

FutureTask是支持传递callback对象以及runnable对象的,不过由于rannable是不支持返回结果,必须传入特定的结果然后返回。所以更多的都是传入callback接口实现的对象
最后使用new Thread(futureTask).start();就可以了

4.1、FutureTask 部分源码说明

  • 1、为什么传递Runnable对象也可以
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        // 创建了一个新的callable的对象,重新包装一层而已,其实是个RunnableAdapter对象而已
        this.state = NEW;       // ensure visibility of callable
    }
    static final class RunnableAdapter implements Callable {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            // 传递的是runnable对象,真正去调用runnable的run方法
            return result;
            // 返回已经设置好的结果
        }
    }
  • 2、FutureTask 的run方法
public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                // 就是在这里调用callable.call 方法
                // 获取计算结果
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
               // ran为true,则意味着存在有效的结果,把result存储到outcome对象中
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

到这里对Java的FutureTask类就有了比较清晰的认识,再次强调一点的是,get方法会长时间轮询,尽量避免在主线程执行该类操作

你可能感兴趣的:(Java 异步编程之 FutureTask)