處理線程池內(nèi)錯(cuò)誤信息打印問(wèn)題

代碼

重寫ThreadPoolExecutor 的 afterExecute方法

private class ExcaptionThreadPoolExecutor extends ThreadPoolExecutor{
        
        public ExcaptionThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
        
        }

        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            //存在錯(cuò)誤,打印到日志
            if(r instanceof FutureTask){
                try{
                    ((FutureTask) r).get();
                }catch (Throwable taskThr){
                    log.error("錯(cuò)誤:",taskThr);
                }
            }
            if(t != null){
                log.error("錯(cuò)誤:",t);
            }
        }
    }

原因

為什么要重寫ThreadPoolExecutor的afterExecute方法,因?yàn)樵诰€程池執(zhí)行run方法時(shí),run方法被try塊包裹并將結(jié)果執(zhí)行到了afterExecute方法,而線程池的afterExecute方法并沒(méi)有任何實(shí)現(xiàn)

這是線程池內(nèi)執(zhí)行的run方法,在finally中執(zhí)行了afterExecute(task, thrown)方法:

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

但是如果執(zhí)行線程池時(shí)使用的是submit方法,而不是execute的話,情況又會(huì)有些不一樣了,那是因?yàn)閳?zhí)行submit的方法時(shí),線程池將我們傳入的Runnable進(jìn)行了封裝,封裝成了FutureTask類

sumbit方法:

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

newTaskFor方法:

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

我們發(fā)現(xiàn)最后線程池執(zhí)行的其實(shí)是FutureTask類,而它對(duì)run又進(jìn)行了一次封裝

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 must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

也就是實(shí)際上我們的run方法是在這個(gè)run方法內(nèi)執(zhí)行的,而線程池的run方法執(zhí)行的實(shí)際上是RunnableFuture的run方法

從上面我們可以看出在發(fā)生catch時(shí)調(diào)用了setException方法,

protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

也就是在setException中,我們發(fā)現(xiàn)它將Throwable賦值給了outcome變量

/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes

這個(gè)變量在注釋中明確的指出了將用來(lái)存放返回值或者異常信息,在注釋中我們發(fā)現(xiàn)The result to return or exception to throw from get() ,使用get方法獲取信息

/**
 * @throws CancellationException {@inheritDoc}
 */
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}

/**
 * Returns result or throws exception for completed task.
 *
 * @param s completed state value
 */
@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);
}

至此我們拿到了信息,這也就解釋了代碼中為什么要判斷Runnable的實(shí)例是否是FutureTask類型,因?yàn)樵擃愋蜎](méi)有報(bào)錯(cuò),它的錯(cuò)誤信息全都在實(shí)例中,不能用線程池的異常捕獲

?著作權(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)容

  • 執(zhí)行多線程并發(fā)任務(wù)的時(shí)候,如果任務(wù)類型相同,一般會(huì)考慮使用線程池,一方面利用了并發(fā)的優(yōu)勢(shì),一方面避免創(chuàng)建大量線程得...
    德彪閱讀 23,740評(píng)論 2 19
  • 線程池作用 相對(duì)于為每個(gè)請(qǐng)求都創(chuàng)建一個(gè)線程,線程池通過(guò)重用現(xiàn)有的線程而不是創(chuàng)建新線程,可以在處理多個(gè)請(qǐng)求時(shí)分?jǐn)傇诰€...
    Java大生閱讀 1,403評(píng)論 0 31
  • 一、引言 我們都知道線程和線程池是Android開發(fā)中很重要的一個(gè)部分。本文會(huì)從Java線程談起,由淺及深總結(jié)在A...
    Yink_Liu閱讀 930評(píng)論 0 3
  • 對(duì)于C語(yǔ)言,懂點(diǎn)軟件的人都不陌生。 一個(gè)C程序,其結(jié)構(gòu)主要為一下內(nèi)容: 預(yù)處理指令 變量 函數(shù) ...
    sevenKun閱讀 384評(píng)論 0 0
  • 哥哥意外溺死在與世隔絕的孤島,雙子弟妹共同上島尋找事實(shí)真相,面臨的卻是苦澀的戀愛(ài)和悲劇的詛咒。。。超級(jí)好看超級(jí)合口...
    薔薇DONO閱讀 1,591評(píng)論 0 95

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