FutureTask学习笔记

11 篇文章 0 订阅
10 篇文章 0 订阅

Future 接口介绍

Future 表示一个异步计算,提供了用于检查异步计算是否完成,等待异步计算结果,取消异步计算任务等方法。等待获取异步计算结果会阻塞当前线程,直到超时或者获取到异步计算的结果。如果异步计算已经完成,取消计算任务的方法将不会生效。如果希望利用 Future 来处理异步但是没有计算结果的任务,可以使用 Future<?>,然后返回 null 实现。

Future 的使用例子如下:

  1. 利用线程池的 submit 方法获取生成 Future,利用 Future 获取异步任务结果
     interface ArchiveSearcher { String search(String target); }
        
     class App {
      ExecutorService executor = ...
      ArchiveSearcher searcher = ...
      void showSearch(final String target)
          throws InterruptedException {
        Future<String> future
          = executor.submit(new Callable<String>() {
            public String call() {
                return searcher.search(target);
            }});
        displayOtherThings(); // do other things while searching
        try {
          displayText(future.get()); // use future
        } catch (ExecutionException ex) { cleanup(); return; }
      }
    }
  1. 自定义 FutureTask,利用线程类执行异步任务,利用 FutureTask 获取异步任务结果
    public class FutureTaskTest {
    
      public static void main(String[] args) throws InterruptedException, ExecutionException {
          long start = System.currentTimeMillis();
    
          Callable<String> callable = () -> {
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              return "任务执行结果";
          };
          FutureTask<String> futureTask = new FutureTask<>(callable);
        
          Thread thread = new Thread(futureTask);
        
          thread.start();
        
          System.out.println(futureTask.get());
      }
    }

Future 的方法列表:

    /**
     * 取消异步任务。
     * 该方法尝试取消异步任务,但是不一定能够取消成功。如果任务已经完成,获取已经被取消过,或者由于其他原因取消了,那么都会取消失败。
     * 如果异步任务在取消的时候还未开始,那么异步任务将不会开始运行;
     * 如果任务已经开始运行,那么 mayInterruptIfRunning 参数将决定是否通过中断线程来取消异步任务。
     *
     * 如果取消成功,后续调用 isDone 和 isCancelled 都将会返回 true。
     *
     * @param mayInterruptIfRunning 参数决定运行中的任务是否通过中断线程来取消任务。如果设置 false,那么运行中的任务无法取消。
     * @return 如果取消成功则返回 true,否则返回 false。
     */
    boolean cancel(boolean mayInterruptIfRunning);
    
    /**
     * 如果异步任务取消成功,那么返回 true,否则返回 false
     *
     * @return 如果任务取消成功返回 true,否则返回 false
     */
    boolean isCancelled();
    
    /**
     * 如果任务执行完毕(包括被取消成功)返回 true,否则返回 false。
     *
     * 任务正常结束,任务被取消成功,任务执行中发生异常而结束都是任务执行完毕。
     * @return 任务执行完毕则返回 true,否则返回 false
     */
    boolean isDone();
    
    /**
     * 等待获取任务计算结果。该方法或中断当前线程,知道异步任务结束。
     *
     * @return 任务计算结果
     * @throws CancellationException 任务如果取消则抛出该异常
     * @throws ExecutionException 任务执行过程中发生异常
     * @throws InterruptedException 执行异步任务的线程被中断
     */
    V get() throws InterruptedException, ExecutionException;
    
    /**
     * 可设置超时时间的等待获取异步任务计算结果。该方法会中断当前线程直到获取异步任务结果或者超时。
     *
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return 异步任务结果
     * @throws CancellationException 任务被取消
     * @throws ExecutionException 异步任务执行过程中发生异常
     * @throws InterruptedException 执行异步任务的线程被中断
     * @throws TimeoutException 等待时间超过设置的超时时间
     */
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;```
<a id="org58d0423"></a>

# RunnableFuture

Future 本身是一个异步计算结果,为了让 Future 成为一个任务,所以定义了一个 RunnableFuture。RunnableFuture 即继承 Runnable,有继承 Future,即表示是一个异步计算结果,也表示一个可执行的一个任务。

```java
    /**
     * RunnableFuture 同时表现成一个异步计算结果和可通过线程执行的任务。
     */
    public interface RunnableFuture<V> extends Runnable, Future<V> {
        /**
         * 如果任务未取消,那么将任务的计算结果交给异步计算结果(Future)
         */
        void run();
    }

FutureTask

介绍

FutureTask 是 RunnableFuture 的具体实现,也是线程池框架执行异步任务的结算结果。

FutureTask 的类继承关系如下:
类继承

原理

FutureTask 有 Callable callable 属性,用于存储外部任务。当初始化 FutureTask 时,需要指定一个具体的外部任务初始化 callable 属性。

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;
}

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

FutureTask 本身是一个任务,提供 run 方法的具体实现。run 方法会执行 callable.call()拿到任务结果,并将结果赋值给另一个属性 Object outcome。

当使用 get()方法获取异步任务结果时,其实就是等待任务 run 方法执行结束:在异步任务未结束前,生成等待节点,加入到等待队列,线程被挂起。如果异步任务执行结束后会给outcome赋值异步任务计算结果,并唤醒等待队列中的线程。

get()方法通过 FutureTask 的状态判断任务是否执行完成,如果是执行完成的状态,那么返回 outcome,如果线程被中断或者超时或者执行任务过程中抛出异常则 get 方法抛出对应的异常。

FutureTask 的状态:

状态

任务执行成功的状态变化示意图:
执行成功的状态变化

代码详情

重要属性

    /**
     * state 表示 FutureTask 的状态。可能的状态一空有 7 种,初始化时是 NEW。状态变化的路径为:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    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;
    
    /** 代表一个外部任务。任务执行完毕后会置为 null */
    private Callable<V> callable;
    /** 存储异步任务执行结果 */
    private Object outcome;
    /** 用于存储执行异步任务的线程的引用, */
    private volatile Thread runner;
    /** 单向链表,用来存储调用 get 方法阻塞等待异步任务执行结果的线程 */
    private volatile WaitNode waiters;```

<a id="org00f89d1"></a>

### 构造方法

FutureTask 初始化需要外部指定的参数是 callable 对象,需要在执行任务时执行 callable.call 获取异步任务结果。所以构造方法都是围绕初始化 callable 对象而进行

```java
    /**
     * 根据指定的 callable 对象生成一个 FutureTask 对象。
     *
     * @param  callable callable 对象
     * @throws NullPointerException 如果 callable 对象是空对象,那么抛出 NullPointerException 异常
     */
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        // FutureTask 初始化状态是 NEW
        this.state = NEW;
    }
    
    /**
     * 根据指定的 Runnable 对象生成 FutureTask。
     * 借助 Executors public static <T> Callable<T> callable(Runnable task, T result)方法,利用 runnable 对象和泛型生成一个 callable 对象。
     *
     * @param runnable 可执行任务
     * @param result callable 的执行结果的类型(执行 callable.call 的返回类型)
     * @throws NullPointerException 如果 runnable 是空对象,那么抛出空指针异常
     */
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }```
<a id="org224cb8b"></a>

### Future 接口实现

```java
    /**
     *  取消异步任务
     * @param mayInterruptIfRunning 参数决定运行中的任务是否通过中断线程来取消任务。如果设置 false,那么运行中的任务无法取消。
     * @return 如果取消成功则返回 true,否则返回 false。
     */
    public boolean cancel(boolean mayInterruptIfRunning) {
        // 如果 FutureTask 状态是 NEW,并且通过中断线程来取消任务,那么将状态改成 INTERRUPTING,即 NEW -> INTERRUPTING -> INTERRUPTED 的状态变化
        // 如果 FutureTask 状态是 NEW,并且不通过中断线程来取消,那么将状态改成 CANCELLED,即 NEW -> CANCELLED 的状态变化流程
        // 如果判断条件不成功,说明此事 FutureTask 已经不是 NEW 的状态,那么直接返回取消失败。
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {
            // 如果是通过中断线程来取消任务,那么将线程置为中断状态,并且将 FutureTask 的状态改成 INTERRUPTED
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            // 唤醒所有等待获取结果的线程
            finishCompletion();
        }
        return true;
    }
    
    /**
     * FutureTask 的异步任务是否已经被取消
     */
    public boolean isCancelled() {
        // 如果 FutureTask 的状态是 CANCELLED,INTERRUPTING,INTERRUPTED 的状态,那么 FutureTask 就是被取消的。INTERRUPTING 或者 INTERRUPTED 的状态可以通过 FutureTask.cancel(true)产生。
        // EXCEPTIONAL 是在执行异步任务过程中抛出的异常产生并非是取消任务产生。
        return state >= CANCELLED;
    }
    
    /**
     * 返回异步是否已经结束。非开始的状态都是结束的装填。
     */
    public boolean isDone() {
        // 在 callable.call 方法结束前,FutureTask 的状态都是 NEW 的状态,call 结束后 FutureTask 的状态才会改成其他状态,所以非 NEW 的状态都是结束状态。
        return state != NEW;
    }
    
    /**
     * 获取异步任务的计算结果
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        // 如果 s <= COMPLETING 说明任务还没执行完成那么等待异步任务完成
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        // 如果正常获取到异步任务计算结果,那么返回异步计算任务结果;如果异步任务被取消,那么抛出 CancellationException;如果异步任务执行过程中发生异常,那么抛出 ExecutionException 异常
        return report(s);
    }
    
    /**
     * 可自定义超时时间的等待获取异步任务计算结果
     */
    public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }
    
    /**
     * 唤醒所有等待获取异步任务结果的线程
     */
    private void finishCompletion() {
        // 如果等待队列中有节点,那么唤醒节点上的线程,并将节点提出等待队列
        for (WaitNode q; (q = waiters) != null;) {
            // 利用 CAS 比较修改,将等待队列置为 null
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                    // 唤醒节点上的线程
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    // 遍历下一个节点
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }
        // 桩代码,FutureTask 没有任何功能,留给子类做完成任务后的定制化处理
        done();
        callable = null;        // to reduce footprint
    }
    
    /**
     * 等待异步任务结束
     *
     * @param timed 是否设置了超时时间
     * @param nanos 超时时间
     * @return state FutureTask 结束 awaitDone 时的的状态
     */
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        // 计算等待的毫秒数
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        // 等待获取异步任务的节点
        WaitNode q = null;
        // 是否进入等待队列
        boolean queued = false;
        for (;;) {
            // 此处应该是是线程阻塞(park 方法能够响应线程中断)时,线程被中断。如果线程被中断,那么从队列中删除对应的等待节点,抛出 InterruptedException
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }
            // FutureTask 当前状态
            int s = state;
            // 如果不是 NEW 那么没有必要继续等待异步任务执行结果
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            // 如果是 COMPLETING,FutureTask 的异步任务即将完成或者被中断结束,那么将 CPU 让渡出去继续等待最终完成。
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            // 需要线程阻塞等待,先构建等待节点
            else if (q == null)
                q = new WaitNode();
            // 如果等待节点未入队,那么将等待节点添加到队列队首,
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            // 如果设置超时时间,那么先判断是否已经超过了超时时间,如果超过则删除等待节点并退出;否则 LockSupport.parkNanos 阻塞线程。
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }
    
    /**
     * 从阻塞队列中删除指定的等待节点
     */
    private void removeWaiter(WaitNode node) {
        if (node != null) {
            node.thread = null;
            retry:
            for (;;) {          // restart on removeWaiter race
                for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                    s = q.next;
                    if (q.thread != null)
                        pred = q;
                    else if (pred != null) {
                        pred.next = s;
                        if (pred.thread == null) // check for race
                            continue retry;
                    }
                    else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                          q, s))
                        continue retry;
                }
                break;
            }
        }
    }

Runnable 的实现

    /**
     * 任务的实现细节
     */
    public void run() {
        // 如果 FutureTask 不是初始化状态或者 runner 已经指向了具体的线程,说明 FutureTask 已经被取消或者已经被执行(run 方法已经被调用过),那么直接退出该方法
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            // c != null && state == NEW 说明任务未被执行并且未被取消
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    // 执行异步任务并获取任务结果
                    result = c.call();
                    ran = true;
                // 如果执行异步任务过程中抛出异常,那么捕获并将 FutureTask 的状态置为 COMPLETING,将异常赋值给 outcome,之后将状态置为 EXCEPTIONAL,即 NEW -> COMPLETING -> EXCEPTIONAL 的变化流程。
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                // 如果正常执行结束,将 FutureTask 状态从 NEW 置为 COMPLETING,将结果赋值给 outcome,将状态置为 NORMAL,即 NEW -> COMPLETING -> NORMAL 状态变化流程。
                if (ran)
                    set(result);
            }
        } finally {
            // 清空 runner
            runner = null;
            int s = state;
            // 再次确认是否有执行通过线程中断来取消操作,如果有那么等待取消完成。
            // s >= INTERRUPTING 可能产生的情况是:在执行 set 操作前 FutureTask 被取消,导致状态从 NEW 编程变成 INTERRUPTING。那么 run 方法需要等待取消操作结束再结束。这是为了确保运行时的 cancel(true)的操作传递过来的取消操作都能在 run()方法结束前处理。
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }/**
     * 如果在执行异步任务过程中捕获到问题,那么将捕获到的异常的原因交给 outcome,并唤醒所有等待获取结果的线程。
     *
     * @param t 异常的原因
     */
    protected void setException(Throwable t) {
        // 如果 FutureTask 的状态是 NEW,那么改成 COMPLETING。
        // 如果在处理异常前一刻异步任务被取消,那么会走取消流程,因为 CAS 修改只针对 NEW 的状态。
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            // 唤醒等待获取异步任务结果的线程
            finishCompletion();
        }
    }
    
    /**
     * 将任务的执行结果赋值给 outcome,并修改 FutureTask 的状态
     *
     * @param v the value
     */
    protected void set(V v) {
        // CAS 比较修改 FutureTask 的状态,从 NEW 改成 COMPLETING.
        // 如果修改成功则将结果赋值给 outcome
        // 再将 FutureTask 的状态改成 NORMAL
        // 唤醒等待获取异步任务计算结果的线程
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
FutureTask和CompletableFuture都是用于异步获取线程执行结果的工具。 FutureTask实现了Future接口,并且还实现了Runnable接口,所以可以直接将FutureTask提交到线程池执行,并且可以获取执行结果。而Future只支持获取Callable的执行结果。所以从功能上来说,FutureTask更加全面且方便。FutureTask的底层是基于AQS(AbstractQueuedSynchronizer)实现的。 为了满足对异步编程的需求,JDK8引入了CompletableFuture。CompletableFuture是在JDK1.8之后出现的,与之前的FutureFutureTask有很大的区别。CompletableFuture的功能更加强大且复杂,融入了大量的流式编程思想。可以等待多个CompletableFuture对应的计算结果都出来后再做一些操作。 总结来说,FutureTask和CompletableFuture都是用于异步获取线程执行结果的工具,但是CompletableFuture的功能更加丰富和灵活。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [FutureFutureTask,CompletableFuture](https://blog.csdn.net/XH413235699/article/details/97302732)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [【异步编程学习笔记】JDK中的FutureTask和CompletableFuture详解(使用示例、源码)](https://blog.csdn.net/qq_41358574/article/details/120521955)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值