您的位置:首页 > 编程语言 > Java开发

Java-线程池异常信息的坑

2016-04-12 00:08 501 查看

Java-线程池异常信息的坑

大家平时肯定用过线程池,不知道有没有踩过这个坑。

线程中的任务出现异常没有任何错误日志信息。

描述

应用启动是通过多线程来初始化的。

但是出现莫名其妙的问题,什么问题呢?初始化卡住,进不了应用!!!

更可气的是,日志中什么错误信息都没有!!!

抓狂中…

分析

Thread.setDefaultUncaughtExceptionHandler截获了???我们使用了很多第三方库,可能被其中某个第三方库给截获了。

Where try catch???

验证

Thread.setDefaultUncaughtExceptionHandler验证。

ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(new Runnable() {
@Override
public void run() {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
e.printStackTrace(); // 没有进入到这里
}
});
throw new IllegalArgumentException("test!!");
}
});


Thread.setDefaultUncaughtExceptionHandler验证失败,没有进入到uncaughtException方法,也就说明,不是因为有人使用Thread.setDefaultUncaughtExceptionHandler截获异常信息的。

Where try catch验证。

Executors.newCachedThreadPool();

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}


ThreadPoolExecutor类

public class ThreadPoolExecutor extends AbstractExecutorService {
}


AbstractExecutorService的submit方法,会调用newTaskFor创建一个RunnableFuture

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


在看newTaskFor方法,最终创建的是一个FutureTask对象

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}


FutureTask的run方法

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);
}
}


终于找到问题了,是FutureTask的run方法中加了try-catch导致无法把异常信息输出。

明确问题后,如何解决?

解决

我们看FutureTask的setException方法是run截获异常后调用的函数

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


finishCompletion方法会调用done方法

private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
...
}
done();
callable = null;        // to reduce footprint
}


done方法

/**
* Protected method invoked when this task transitions to state
* {@code isDone} (whether normally or via cancellation). The
* default implementation does nothing.  Subclasses may override
* this method to invoke completion callbacks or perform
* bookkeeping. Note that you can query status inside the
* implementation of this method to determine whether this task
* has been cancelled.
*/
protected void done() { }


实现自己的FutureTask

public class MyFutureTask extends FutureTask<Object> {
public MyFutureTask(Runnable r) {
super(r, null);
}
@Override
protected void done() {
try {
if (!isCancelled()) get();
} catch (ExecutionException e) {
// Exception occurred, deal with it
System.out.println("Exception: " + e.getCause()); // 输出错误信息
} catch (InterruptedException e) {
// Shouldn't happen, we're invoked when computation is finished
throw new AssertionError(e);
}
}
}


总结

个人总结解决问题的方式

分析-假设可能情况。

验证-根据假设的可能情况来验证是否成立。

解决-找到问题原因后,再想方法解决问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程池