FutureTask 学习

6 篇文章 0 订阅

在 Java 中一般通过继承 Thread 类或者实现 Runnable 接口这两种方式来创建多线程,但是这两种方式都有个缺陷,就是不能在执行完成后获取执行的结果,因此Java 1.5 之后提供了 Callable 和 Future 接口,通过它们就可以在任务执行完毕之后得到任务的执行结果。

Future Demo
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; }
    }
}}

FutureTask 是一个支持取消行为的异步任务执行器。该类实现了 Future 接口的方法:

  1. 取消任务执行。
  2. 查询任务是否执行完成。
  3. 获取任务执行结果(get 任务必须得执行完成才能获取结果,否则会阻塞直至任务完成)。
FutureTask Demo
FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
     public String call() {
       return searcher.search(target);
    }});
    
    executor.execute(future);
}

继承关系

在这里插入图片描述

FutureTask 实现了 RunnableFuture 接口, RunnableFuture 接口继承了 Runnable、Future 接口。

Future 接口

public interface Future<V> {
    /**
     * 取消任务
     *
     * @param mayInterruptIfRunning 是否允许取消正在执行却没有执行完毕的任务
     * @return true-取消任务成功,false-取消任务失败
     */
    boolean cancel(boolean mayInterruptIfRunning);
    
    /**
     * 任务是否被取消成功
     */
    boolean isCancelled();
    
    /**
     * 任务是否已经完成
     */
    boolean isDone();
    
    /**
     * 获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回
     */
    V get() throws InterruptedException, ExecutionException;
    
    /**
     * 获取执行结果,如果在指定时间内,还没获取到结果,就直接返回 null
     */
    V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
}

内部任务运行状态属性

state 可能存在的状态转换

  • 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; // 表示任务执行结束(正常执行结束,或者发生异常结束),但是还没有将结果保存到 outcome 中。中间状态
private static final int NORMAL = 2; // 表示任务正常执行结束,并且已经把执行结果保存到 outcome 字段中。最终状态
private static final int EXCEPTIONAL = 3; // 表示任务发生异常结束,异常信息已经保存到 outcome 中。最终状态。
private static final int CANCELLED = 4; // 任务执行结束之前被取消了,但是不要求中断正在执行的线程,也就是调用了 cancel(false)。最终状态
private static final int INTERRUPTING = 5; // 任务在新建之后,执行结束之前被取消了,并要求中断线程的执行,也就是调用了 cancel(true),这时任务状态就是 INTERRUPTING。中间状态。
private static final int INTERRUPTED = 6; // 调用 cancel(true) 取消异步任务,会调用 interrupt() 中断线程的执行,然后状态会从 INTERRUPTING 变到 INTERRUPTED。最终状态。

其他属性

/**
 * 将要执行的任务
 */
private Callable<V> callable;
/**
 * 用来保存计算任务的返回结果,或者执行过程中抛出的异常
 */
private Object outcome;
/**
 * 执行任务的执行线程
 */
private volatile Thread runner;
/**
 * 等待节点,如果任务还没有执行结束,那么调用 get() 获取结果的线程会阻塞,在这个阻塞队列中排队等待
 */
private volatile WaitNode waiters;

内部节点

等待节点,栈结构的等待队列

static final class WaitNode {
    volatile Thread thread;
    volatile WaitNode next;

    WaitNode() {
        thread = Thread.currentThread();
    }
}

构造方法

允许传入 Callable、Runnable 类型

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // 新任务状态
}

/**
 * 将 Runnabl 转化为 Callable,这里使用了适配器模式实现
 */
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // 新任务状态
}

任务执行:run()

FutureTask 封装了计算任务,无论是提交给 Thread 执行,或者线程池执行,调用的都是 FutureTask.run()

run() 执行一个任务,会被线程自动调用执行。

public void run() {
    if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                    null, Thread.currentThread()))
        // state !=NEW,说明任务已经被其他线程执行,甚至执行结束,或者被取消了,直接返回
        // 判断执行任务的线程对象 runner 是否为空,为空就将当前执行线程赋值给 runner 属性,不为空说明已经有线程准备执行这个任务了

        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran; // 任务执行完毕标识
            try {
                // 执行任务,返回结果
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                // 抛出异常
                setException(ex);
            }
            if (ran)
                // 任务执行完毕就设置结果
                set(result);
        }
    } finally {

        // 将执行任务的执行线程置空
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            // 任务处于中断中的状态,则进行中断操作
            handlePossibleCancellationInterrupt(s);
    }
}

/**
 * 设置任务执行成功状态,最终状态 NORMAL
 */
protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion();
    }
}

/**
 * 设置任务执行异常,最终状态 EXCEPTIONAL
 */
protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = t;
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        finishCompletion();
    }
}

/**
 * 判断是否在执行的过程中被中断了,如果被中断,处理中断
 */
private void handlePossibleCancellationInterrupt(int s) {
    if (s == INTERRUPTING)
        while (state == INTERRUPTING)
            // 表示正在被中断,让出线程的执行权,给其他线程来执行
            Thread.yield(); 

    // assert state == INTERRUPTED;

    // We want to clear any interrupt we may have received from
    // cancel(true).  However, it is permissible to use interrupts
    // as an independent mechanism for a task to communicate with
    // its caller, and there is no way to clear only the
    // cancellation interrupt.
    //
    // Thread.interrupted();
}

/**
 * 在任务执行完成(包括取消、正常结束、发生异常),将等待线程列表唤醒
 **/
private void finishCompletion() {
    // assert state > COMPLETING;
    for (WaitNode q; (q = waiters) != 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;
        }
    }

    done(); // 这里可以自定义实现任务完成后要做的事(在子类中重写 done() 方法)

    // 任务执行完,释放资源
    callable = null;
}

/**
 * 这个方法什么都没有做,不过子类可以实现这个方法,做一些额外的操作
 */
protected void done() {
}

runAndReset() 任务可被多次执行, 这个方法同 run() 方法的区别在于这个方法不会设置任务的执行结果值,而只是执行任务完之后,将任务的状态重新设置为初始化的可被执行状态。

protected boolean runAndReset() {
    if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                    null, Thread.currentThread()))
        return false;
    boolean ran = false;
    int s = state;
    try {
        Callable<V> c = callable;
        if (c != null && s == NEW) {
            try {
                c.call(); // don't set result
                ran = true;
            } catch (Throwable ex) {
                setException(ex);
            }
        }
    } finally {
        runner = null;
        s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
    return ran && s == NEW;
}

任务取消:cancel()

当调用 cancel 方法被调用时:如果任务还没有执行,那么这个任务就不会执行了;如果任务已经执行,且 mayInterruptIfRunning=true,那么执行任务的线程会被中断。

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
            UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                    mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        // state !=NEW,说明任务已经结束或者被取消了
        // state ==NEW,mayInterruptIfRunning=true,说明要中断任务的执行,NEW->INTERRUPTING,mayInterruptIfRunning=false,不需要中断,状态改为CANCELLED

        return false;
    try {
        if (mayInterruptIfRunning) {
            try {
                // 读取当前正在执行任务的线程 runner 并中断
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally {
                // 修改状态为 INTERRUPTED
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}

结果返回:get()

获取任务结果,如果这个任务还没有执行结束并且未设置超时,则调用线程会进入阻塞状态。

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    // state <= COMPLETING,说明任务还没开始执行或还未执行完成,调用 awaitDone() 阻塞该调用线程
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}

/**
 * 返回任务结果或任务异常
 * <p>
 * 读取 outcome,将 state 映射到最后返回的结果中
 * s == NORMAL 说明任务正常结束,返回正常结果,s >= CANCELLED 抛出 CancellationException
 */
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)
        return (V) x;
    if (s >= CANCELLED)
        throw new CancellationException();
    throw new ExecutionException((Throwable) x);
}

/**
 * 阻塞等待
 */
private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    for (; ; ) {
        // 1. 如果该线程执行 interrupt() 方法,则从队列中移除该节点,并抛出异常
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        if (s > COMPLETING) {
            // 2. 任务执行完成或取消,返回完成状态
            if (q != null)
                q.thread = null;
            return s;
        } else if (s == COMPLETING)
            // 3. 说明正在 set 结果,此时让线程稍等一下
            Thread.yield();
        else if (q == null)
            // 构造一个等待节点 WaitNode
            q = new WaitNode();
        else if (!queued)
            // 4. 将当前线程加入到 FutureTask 的等待线程队列首节点中去
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                    q.next = waiters, q);
        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;
        }
    }
}

/**
 * 指定超时,超时之后会抛出 TimeoutException
 */
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);
}

任务状态方法

/**
 * 返回是否被取消
 */
public boolean isCancelled() {
    return state >= CANCELLED;
}

/**
 * 返回是否执行完成
 */
public boolean isDone() {
    return state != NEW;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值