并發(fā)編程之submit和execute區(qū)別(七)

前言

使用線程池難免會(huì)用到submit和execute,但是submit是有坑的,此處做個(gè)記錄

1、submit坑

此處隨便寫一個(gè)方法,進(jìn)入內(nèi)部查看execute和submit

/**
 * @Author: 小混蛋
 * @CreateDate: 2018/8/29 9:58
 */
@Component
public class Test {
    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(5);
        ArrayList<Future<?>> arrayList = new ArrayList();
        for (int i = 0; i < 10; i++) {
            final int b = i;
            Future<?> submit = es.submit(() -> {
                System.out.println(Thread.currentThread().getName());
                int a = b / 0;
            });
            arrayList.add(submit);
        }
        arrayList.forEach(s -> {
            try {
                s.get();
            } catch (InterruptedException |ExecutionException e) {
                e.printStackTrace();
            }
        });
        es.shutdown();
    }
    @Scheduled(cron = "")
    public void test() {

    }
}

ctrl加鼠標(biāo)左鍵進(jìn)入submit,查看AbstractExecutorService,發(fā)現(xiàn)submit底層調(diào)用的還是execute,但是提交的任務(wù)不是task,而是在task的基礎(chǔ)上封裝了一層FutureTask

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

重點(diǎn)來(lái)了,當(dāng)submit提交的task里面出現(xiàn)未檢查異常如RuntimeException和Error等,直接execute你的task肯定是拋異常;但是使用submit之后提交的FutureTask我們看下它的源碼run方法:run方法和我們直接提交的task的run方法并不一樣,該方法會(huì)對(duì)所有的Throwable類型進(jìn)行捕獲,并把異常通過(guò)setException保存在內(nèi)部變量outcome里面。所以線程池執(zhí)行的過(guò)程中異常不會(huì)被拋出

public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            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);
        }
    }
protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

另一個(gè)重點(diǎn)來(lái)了,當(dāng)submit被futuretask.get的時(shí)候。會(huì)在report方法調(diào)用過(guò)程中拋出這個(gè)未檢查異常!

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
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);
    }

結(jié)論

1、submit在執(zhí)行過(guò)程中與execute不一樣,不會(huì)拋出異常而是把異常保存在成員變量中,在FutureTask.get阻塞獲取的時(shí)候再把異常拋出來(lái)。
2、Spring的@Schedule注解的內(nèi)部實(shí)現(xiàn)就是使用submit,因此,如果你構(gòu)建的任務(wù)內(nèi)部有未檢查異常,你是永遠(yuǎn)也拿不到這個(gè)異常的。
3、execute直接拋出異常之后線程就死掉了,submit保存異常線程沒(méi)有死掉,因此execute的線程池可能會(huì)出現(xiàn)沒(méi)有意義的情況,因?yàn)榫€程沒(méi)有得到重用。而submit不會(huì)出現(xiàn)這種情況。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容