欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 宝鼎售后问题提交 | 后台管理


新闻资讯

MENU

软件开发知识

该过程可抛出 图纸加密 InterruptedException退出

点击: 次  来源:宝鼎软件 时间:2017-11-13

原文出处: 猴子007

线程池的实现焦点之一是FutureTask。在提交任务时,用户实现的Callable实例task会被包装为FutureTask实例ftask;提交后任务异步执行,无需用户体贴;当用户需要时,再挪用FutureTask#get()获取功效——或异常。

随之而来的问题是,如何优雅的获取ftask的功效并处理惩罚异常?本文接头利用FutureTask的正确姿势。

本日换个气势气魄。

源码阐明

从提交一个Callable实例task开始。

submit()

ThreadPoolExecutor直接担任AbstractExecutorService的实现。

public abstract class AbstractExecutorService implements ExecutorService {
...
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
...
}

后续流程可参考源码|从串行线程关闭到工具池、线程池。最终会在ThreadPoolExecutor#runWorker()中执行task.run()。

task即5行建设的ftask,看newTaskFor()。

newTaskFor()

AbstractExecutorService#newTaskFor()建设一个RunnableFuture范例的FutureTask。

public abstract class AbstractExecutorService implements ExecutorService {
...
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
...
}

看FutureTask的实现。

FutureTask

public class FutureTask<V> implements RunnableFuture<V> {
...
    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;
...
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
...
}

结构要领的重点是初始化ftask状态为NEW。

状态机

状态转换较量少,直接给状态序列:

* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED

状态在后头有用.

run()

简化如下:

public class FutureTask<V> implements RunnableFuture<V> {
...
    public void run() {
        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);
        }
    }
...
}

假如执行时未抛出异常

假如未抛出异常,则ran==true,FutureTask#set()配置功效。

public class FutureTask<V> implements RunnableFuture<V> {
...
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
...
}
  • outcome中生存功效result
  • 持续两步配置状态到NORMAL
  • finishCompletion()执行一些清理
  • 记着outcome。

    相当于4行获取独有锁,5-6行执行锁中的操纵(留意,7行是不加锁的)。

    假如执行时抛出了异常

    假如运行时抛出了异常,则被12行catch捕捉,昆山软件开发,昆山软件开发,FutureTask#setException()配置功效;同时,ran==false,因此不执行FutureTask#set()。

    public class FutureTask<V> implements RunnableFuture<V> {
    ...
        protected void setException(Throwable t) {
            if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
                outcome = t;
                UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
                finishCompletion();
            }
        }
    ...
    }
  • outcome中生存异常t
  • 持续两步配置状态到EXCEPTIONAL
  • finishCompletion()执行一些清理
  • 假如没有抛出异常在,则outcome记录正常功效;假如抛出了异常,则outcome记录异常。

    假如认为正常功效和异常都属于“任务的输出”,则利用沟通的变量outcome记录是公道的;同时,利用差异的竣事状态区分outcome中记录的内容。

    RUN()小结

    FutureTask将用户实现的task封装为ftask,利用状态机和outcome打点ftask的执行进程。这些进程对用户是不行见的,直到用户挪用get()要领。

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