ThreadPoolExecutor(七)——总结&补充
2016-07-24 18:21
351 查看
1.关于异常
线程池在执行execute方法和submit任务的时候,如果任务中抛出了异常,外部线程无法感知,所以要做一些措施来进行处理,下面就说一下产生这种效果的原因以及一些可用的处理方法。关于异常的处理内容参考了文章并进行了相关补充:ExecutorService-10个要诀和技巧
1.execute执行的任务抛出异常
1.原理
测试代码:public static void main(String[] args) { Executors.newFixedThreadPool(1); try { Executors.newFixedThreadPool(1).execute(new Runnable() { @Override public void run() { int i=1/0; } }); } catch (Exception e) { System.out.println("catch!!!!"); } finally { System.out.println("normal"); } }这个异常会被内部吞掉的,不会被catch块catch到。
但是程序会用System.err和e.printStackTrace方式打印异常栈信息,是由下面这个方法触发的:
/** * Dispatch an uncaught exception to the handler. This method is * intended to be called only by the JVM. */ private void dispatchUncaughtException(Throwable e) { getUncaughtExceptionHandler().uncaughtException(this, e); }看注释,这个方法是用来分发处理一个uncaught的异常交由UncaughtExceptionHandler来处理,是由JVM底层来调用的。
具体的实现在ThreadGroup类中,有个uncaughtException方法,
/** * Called by the Java Virtual Machine when a thread in this * thread group stops because of an uncaught exception, and the thread * does not have a specific {@link Thread.UncaughtExceptionHandler} * installed. * <p> * Applications can override this method in subclasses of * <code>ThreadGroup</code> to provide alternative handling of * uncaught exceptions. * * @param t the thread that is about to exit. * @param e the uncaught exception. * @since JDK1.0 */ public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else { Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null) { ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { System.err.print("Exception in thread \"" + t.getName() + "\" "); e.printStackTrace(System.err); } } }先看注释,该方法是由JVM调用,当一个线程由于一个uncaught异常而导致线程终止,而且该线程没有指定一个UncaughtExceptionHandler。
在else if块中会用System.err和e.printStackTrace配合打印异常信息。
这种情况为了保证我们的程序在运行时能够感知异常,我们可以在我们的task的run方法中用try catch把语句包起来,然后在catch记录日志和监控。注意这里既是把线程包装一下重新跑出去也是没用的。
2.解决方案
自己把线程封装一下/** * 第一种实现自己定义线程,同时自己定义一个ThreadFactory的实现呢 */ static class MyAppThread extends Thread { public static final String DEFAULT_NAME = "MyAppThread"; private static final AtomicInteger created = new AtomicInteger(); private static final AtomicInteger alive = new AtomicInteger(); public MyAppThread(Runnable r) { this(r, DEFAULT_NAME); } public MyAppThread(Runnable runnable, String name) { super(runnable, name + "-" + created.incrementAndGet()); this.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { logger.error("UNCAUGHT1 in thread " + t.getName(), e); } }); } public void run() { System.out.println("Created " + getName()); try { alive.incrementAndGet(); super.run(); } finally { alive.decrementAndGet(); System.out.println("Exiting " + getName()); } } public static int getThreadsCreated() { return created.get(); } public static int getThreadsAlive() { return alive.get(); } }自己封装线程的工厂类
public class MyThreadFactory2 { public Thread newThread(Runnable runnable) { Thread ret = Executors.defaultThreadFactory().newThread(runnable); ret.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { logger.error("UNCAUGHT2 in thread " + t.getName(), e); } }); return ret; } }在构造线程池的时候,作为参数传递我们自己封装的线程工厂。
2.submit方法提交的任务中抛出异常
/** * @throws RejectedExecutionException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }线程池的submit方法提交的任务是一个FutureTask,它既含有Runnable的任务属性也含有Future的属性。FutureTask的run方法中调用的是内部实现的AbstractQueuedSynchronizer的innerRun方法,
void innerRun() { if (!compareAndSetState(READY, RUNNING)) return; runner = Thread.currentThread(); if (getState() == RUNNING) { // recheck after setting thread V result; try { result = callable.call(); } catch (Throwable ex) { setException(ex); return; } set(result); } else { releaseShared(0); // cancel } }所以call方法抛出异常的话,会被catch捕获并用setException进行异常的设置。在设置异常之后,用future的get(内部是Sync的innerGet方法)方法获取结果的时候会抛出异常,
V innerGet() throws InterruptedException, ExecutionException { acquireSharedInterruptibly(0); if (getState() == CANCELLED) throw new CancellationException(); if (exception != null) throw new ExecutionException(exception); return result; }所以为了确保外部感知到这个异常,有两个方法:
1.一定要是用future的阻塞的get方法来获取结果。如果调用带超时时间的get方法,如果异常在超时之后发生的话,外部也无法感知到这个异常。如果业务上在超时之后已经不关心这个异常了的话,也可以只对超时进行监控和日志输出。
2.对call方法内部进行try catch包裹,在有异常发生的时候,设置一个特殊值来表示异常发生情况下的返回,同时记录日志和监控。
相关文章推荐
- Vim常用快捷键整理
- awk个人笔记
- Java ArrayList源码分析
- 线性表
- 原画视频笔记
- 【Leetcode】39
- poj 2533 Longest Ordered Subsequence
- ubuntu开启SSH服务和允许root远程SSH登录
- Linux系统开机启动过程
- asp.net MVC - 小鸟系列之属性验证
- Windbg快捷键
- Windows8.1自定义快捷方式添加到开始屏幕
- Web和移动可用性设计秘笈
- 基于IAR的STM32v3.5库工程建立
- 你的成就只能代表着你过去的努力程度
- 图论最短路问题和最小生成树问题的区别
- 关于作用域
- Linux 网络配置
- HDU 1358 Period (kmp求循环节)
- 友善之臂tiny4412-1306开发板安卓系统烧写