设为首页 加入收藏

TOP

使用 FutureTask 的正确姿势(二)
2017-11-13 14:55:22 】 浏览:472
Tags:使用 FutureTask 正确 姿势
些清理

如果没有抛出异常在,则outcome记录正常结果;如果抛出了异常,则outcome记录异常。

如果认为正常结果和异常都属于“任务的输出”,则使用相同的变量outcome记录是合理的;同时,使用不同的结束状态区分outcome中记录的内容。

RUN()小结

FutureTask将用户实现的task封装为ftask,使用状态机和outcome管理ftask的执行过程。这些过程对用户是不可见的,直到用户调用get()方法。

顺道明白了Callable实例是如何执行的,为什么实现Callable#call()方法时可以将受检异常抛到外层(而Runable#run()方法则必须在方法内处理,不能抛出)。

get()

public class FutureTask<V> implements RunnableFuture<V> {
...
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
...
}
  • 5行利用定义状态的实际值判断ftask是否已完成,如果未完成(NEW、COMPLETING),则wait阻塞直到完成,该过程可抛出InterruptedException退出。
  • 待ftask完成后,调用report()报告结束状态。

5行的写法不可读,摒弃。

report()

public class FutureTask<V> implements RunnableFuture<V> {
...
    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);
    }
...
}
  • 如果结束状态为NORMAL,则outcome保存了正常结果,泛型强转,返回。
  • 7行利用定义状态的实际值判断ftask是否是被取消导致结束的(CANCELLED、INTERRUPTING、INTERRUPTED),如果是,则将抛出CancellationException。
  • 如果不是被取消的,就是执行过程中task自己抛出了异常,则outcome保存了该异常t,包装返回ExecutionException。

将异常t作为ExecutionException的cause包装起来,异常阅读方法参考你真的会阅读Java的异常信息吗?

CANCELLATIONEXCEPTION和EXECUTIONEXCEPTION

  • CancellationException是非受检异常,原则上可以不处理,但仍然建议处理。
  • ExecutionException是受检异常,在外层必须处理。

源码小结

  • 实现Callable#.call()时可以将受检异常抛到外层。
  • 不管实现Callable#.call()时是否抛出了受检异常,都要在FutureTask#get()时捕获ExecutionException;建议捕获CancellationException。
  • FutureTask#get()中调用了阻塞方法,因此还需要捕获InterruptedException。
  • CancellationException异常中不会给出取消原因,包括是否因为被中断。
  • 工程上建议使用超时版的FutureTask#get(),超时会抛出TimeoutException,需要处理。

反观Future#get()的API声明:

public interface Future<V> {
...
    /**
     * Waits if necessary for the computation to complete, and then
     * retrieves its result.
     *
     * @return the computed result
     * @throws CancellationException if the computation was cancelled
     * @throws ExecutionException if the computation threw an
     * exception
     * @throws InterruptedException if the current thread was interrupted
     * while waiting
     */
    V get() throws InterruptedException, ExecutionException;
...
}

right。

一种正确姿势

给出一种比较全面的正确姿势,仅供参考。

int timeoutSec = 30;
try {
  MyResult result = ftask.get(timeoutSec, TimeUnit.SECONDS);
} catch (ExecutionException e) {
  Throwable t = e.getCause();
  // handle some checked exceptions
  if (t instantanceof IOExcaption) {
    xxx;
  } else if (...) {
    xxx;
  } else { // handle remained checked exceptions and unchecked exceptions
    throw new RuntimeException("xxx", t);
  }
} catch (CancellationException e) {
  xxx;
  throw new UnknownException(String.format("Task %s canceled unexpected", taskId));
} catch (TimeoutException e) {
  xxx;
  LOGGER.error(String.format("Timeout for %ds, trying to cancel task: %s", timeoutSec, taskId));
  ftask.cancel();
  LOGGER.debug(String.format("Succeed to cancel task: %s" % taskId));
} catch (InterruptedException e) {
  xxx;
}
  • 根据实际需求删减。
  • 猴子喜欢在一些语义模糊的地方加assert或抛出UnknownException代替注释。
  • 对InterruptedException的处理暂时不讨论(少有的用于控制流程的异常,猴子理解的有点模糊),读者可参考处理 InterruptedException
首页 上一页 1 2 下一页 尾页 2/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇一道题看清动态规划的前世今生 ( .. 下一篇Java 小技巧 ( 一 ) : 远程 debug

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目