处理线程池内错误信息打印问题

代码

重写ThreadPoolExecutor 的 afterExecute方法

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

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

原因

为什么要重写ThreadPoolExecutor的afterExecute方法,因为在线程池执行run方法时,run方法被try块包裹并将结果执行到了afterExecute方法,而线程池的afterExecute方法并没有任何实现

这是线程池内执行的run方法,在finally中执行了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);
        }
    }

但是如果执行线程池时使用的是submit方法,而不是execute的话,情况又会有些不一样了,那是因为执行submit的方法时,线程池将我们传入的Runnable进行了封装,封装成了FutureTask类

sumbit方法:

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

newTaskFor方法:

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

我们发现最后线程池执行的其实是FutureTask类,而它对run又进行了一次封装

public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable 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);
        }
    }

也就是实际上我们的run方法是在这个run方法内执行的,而线程池的run方法执行的实际上是RunnableFuture的run方法

从上面我们可以看出在发生catch时调用了setException方法,

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

也就是在setException中,我们发现它将Throwable赋值给了outcome变量

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

这个变量在注释中明确的指出了将用来存放返回值或者异常信息,在注释中我们发现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的实例是否是FutureTask类型,因为该类型没有报错,它的错误信息全都在实例中,不能用线程池的异常捕获

你可能感兴趣的:(处理线程池内错误信息打印问题)