处理线程池内错误信息打印问题
代码
重写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类型,因为该类型没有报错,它的错误信息全都在实例中,不能用线程池的异常捕获
推荐阅读
- C多线程|C多线程 队列
- Notification的多线程安全问题
- 游泳池漏水怎么处理()
- Java|Java OpenCV图像处理之SIFT角点检测详解
- 事件处理程序
- Linux下面如何查看tomcat已经使用多少线程
- 多线程NSOperation
- 爬虫数据处理HTML转义字符
- Android|Android BLE蓝牙连接异常处理
- 【冷处理】亲子时间管理检视Day63