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

代码 重写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类型,因为该类型没有报错,它的错误信息全都在实例中,不能用线程池的异常捕获

    推荐阅读