ThreadPoolExecutor submit和execute方法的区别

结论

今天先说结论吧:

  • execute:1.只能提交Runnable接口的对象,我们知道Runnable接口的run方法是没有返回值的。2.execute方法提交的任务异常是直接抛出的
  • submit: 1.可以提交Runnable也可以提交Callable对象,即可以有返回值,也可以没有。2.submit方法是是捕获了异常的,当调用FutureTask的get方法时,才会抛出异常。
    例子还是在github中

先对比api

//只接受Runnable对象没有返回值
public void execute(Runnable command)

//接受Runnable对象并返回Future对象,调用Future的get时,正常完成后,返回null
public Future<?> submit(Runnable task)

//接受Runnable对象并返回Future对象,调用Future的get时,正常完成后,返回指定的result对象
public <T> Future<T> submit(Runnable task, T result)

//接受Callable对象并返回Future对象,调用Future的get时,正常完成后,返回task的结果
public <T> Future<T> submit(Callable<T> task)

execute方法上篇文章已经分析过了,这篇我们主要看下submit方法的实现,submit三个方法实现类似,我们用其中一个举例。

public Future<?> submit(Runnable task) {
    //task对象不能为空
    if (task == null) throw new NullPointerException();
    //将task构造成一个RunnableFuture对象
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    //调用execute方法执行RunnableFuture对象
    execute(ftask);
    //返回future对象
    return ftask;
}

可以看到,submit复用了execute方法,submit仅仅将传入的Runnable或者Callable构造成一个RunnableFuture,我们一起看看newTaskFor方法

//构造FutureTask返回
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}

可以看到主要的不同,就是这个FutureTask,我们看看FutureTask吧。

public class FutureTask<V> implements RunnableFuture<V> {
    
}

public interface RunnableFuture<V> extends Runnable, Future<V> 

通过上面可以看到FutureTask是实现了Runnable和Future两个接口,我们知道Future接口提供了cancel,isCancelled,isDone,get方法,用于获取子线程执行的结果。

FutureTask类结构

public class FutureTask<V> implements RunnableFuture<V> {
    /**
     * 可能出现的状态路径
     * 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; //任务被取消, 注意任务只有在new状态的时候,才可以被取消
    private static final int INTERRUPTING = 5; //任务到中断状态的中间态
    private static final int INTERRUPTED  = 6; //任务被中断

    //执行任务的callable对象,可以看到传入的runnable也会被构造成callable
    private Callable<V> callable;
    //正常返回时的结果或者任务抛出的异常
    private Object outcome; // non-volatile, protected by state reads/writes
    //运行callable的线程
    private volatile Thread runner;
    //等待的线程节点,调用get方法时,如果线程没有运行结束,需要进入等待队列进行等待
    private volatile WaitNode waiters;
    。。。
}

我们上面看到ThreadPoolExecutor调用的是传入Runnable的构造方法。

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}
public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}

可以看到构造方法中将传入的Runnable对象,适配成了RunnableAdapter对象,另外将state设置成NEW的状态。
通过上篇的分析,我们知道execute最终执行的是Runnable的run方法,在这里即FutureTask的run方法。

public void run() {
    //状态不为初始状态或者CAS替换runner为本线程失败,都直接返回,说明任务被别人处理了
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        //将runner设置为自己后,任务被自己抢到了
        //任务不为空且是初始状态
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                //调用Callable对象的call方法
                result = c.call();
                //正常执行完成
                ran = true;
            } catch (Throwable ex) {
                //Callable对象的call方法抛出了异常
                result = null;
                ran = false;
                //将异常对象放到outcome属性中,标记任务为异常状态,唤醒等待的线程
                //这里可以看到对异常进行了捕获,也就是文章开头的结论2
                setException(ex);
            }
            //如果Callable对象的call方法正常执行完成
            //将正常结果放到outcome属性中,标记任务为正常完成的状态,唤醒等待的线程
            if (ran)
                set(result);
        }
    } finally {
        
        runner = null;
        // 再次处理INTERRUPTING状态
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

setException 和set方法是两种不同情况的结束,但代码类似,我们看看正常结束的set方法

protected void set(V v) {
    //标记state为COMPLETING状态
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        //将正常结果赋值给outcome
        outcome = v;
        //标记状态为NORMAL(正常结束)
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        //唤醒等待线程
        finishCompletion();
    }
}

finishCompletion方法

private void finishCompletion() {
    // assert state > COMPLETING;
    //循环唤起全部的等待线程,后面会看到其他线程调用get方法,如果任务还没完成会进入等待队列
    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();

    callable = null;        // to reduce footprint
}

前面我们看到set方法或者setException最后都会进行唤醒挂起的线程,现在就一起看看get方法

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    //如果任务的状态为未完成的状态,进行等待
    //注意:这里的等待线程和执行任务的线程不是同一个哦,可以想象主线程new 线程池执行任务,一般都是在主线程中获取任务的结果,就是调用的get方法
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    //任务完成以后,根据任务的状态,返回正常的执行结果,或者    
    return report(s);
}

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 (;;) {
        //响应中断的等待
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        //如果任务已经完成直接返回
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        // 任务在中间态,说明应该很快就能出结果,做一个让步,就不在挂起了
        else if (s == COMPLETING) 
            Thread.yield();
        else if (q == null) //构造一个等待节点
            q = new WaitNode();
        else if (!queued)
            //将等待节点入队
            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
            //挂起自己, 这里就和finishCompletion方法里的LockSupport.unpark对上了
            LockSupport.park(this);
            //注意:外层无限循环的操作,所以串起来就是构造一个等待节点,再次进入for循环,将节点进入等待队列,再次进入for循环挂起自己。
            //当被唤醒后,再次进入for循环,判断state> COMPLETING, 方法返回
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342