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); } } }
总结
个人总结解决问题的方式分析-假设可能情况。
验证-根据假设的可能情况来验证是否成立。
解决-找到问题原因后,再想方法解决问题。
相关文章推荐
- C#多线程学习之(四)使用线程池进行多线程的自动管理
- c++线程池实现方法
- C语言实现支持动态拓展和销毁的线程池
- c++实现简单的线程池
- 深入java线程池的使用详解
- java中通用的线程池实例代码
- Java编程中线程池的基本概念和使用
- C#线程处理系列之线程池中的I/O线程
- C#线程池操作方法
- C#线程池用法详细介绍
- C++线程池的简单实现方法
- MySQL的线程池原理学习教程
- 深入解析C++编程中线程池的使用
- Node.js事件循环(Event Loop)和线程池详解
- Android编程自定义线程池与用法示例
- c++实现简单的线程池
- 论Java Web应用中调优线程池的重要性
- 支持生产阻塞的Java线程池
- 四种Java线程池用法解析
- Python实现线程池代码分享