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 threadV 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包裹,在有异常发生的时候,设置一个特殊值来表示异常发生情况下的返回,同时记录日志和监控。

ThreadPoolExecutor(七)——总结补充相关推荐

  1. Android-JNI开发系列《七》补充jni与java的数据类型的对应关系和数据类型描述符

    人间观察 人只有不为生存而烦恼的时候,才会追求真正想要的东西. 在前面的几篇文章中有涉及到Java和JNI的通信,比如异常回调,Java和JNI的互相调用等.其中都免不了在通信过程中需要知道Java基 ...

  2. 疯狂java讲义第七章补充习题第12题答案

    12.使用Calendar的各种方法 变量 ALL_STYLES AM AM_PM APRIL areFieldsSet AUGUST DECEMBER DST_OFFSET ERA FEBRUARY ...

  3. 古月居《ROS入门21讲》课程补充资料

    一 Linux 安装与入门 不同用户需要安装的 Ubuntu 版本不同,这里给出不同版本的安装与 Linux 快速入门的教程. 1.1 Ubuntu 安装 虚拟机:https://blog.csdn. ...

  4. Java基础语法(七)——类和对象

    文章目录 Java基础语法(七)--类和对象 一.类和对象的初步认识 二. 类和类的实例化 1.类的定义 2.实例化对象 3.如何使用类中的数据 三. 类的成员 1. 字段/属性/成员变量 (1)实例 ...

  5. Java SE加强篇——超详细,Java入门,这一篇就够了

    建议先阅读 JavaSE基础篇 第一天:面向对象进阶一 一.static静态 关键字 1.static是什么? static是静态的意思,可以修饰成员变量和成员方法 static修饰成员变量表示该成员 ...

  6. day16多线程网络编程日志枚举

    多线程&网络编程 一.实现多线程 1.1 相关概念 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的一条执行路径.实际运作单位.简单理解:应用软件中互相独立,可以同时运 ...

  7. 2018-3-27 遗传算法中的轮盘赌

    原出处:http://my.oschina.net/u/1412321/blog/192454 一.遗传算法的应用 函数优化(遗传算法的经典应用领域): 组合优化(实践证明,遗传算法对于组合优化中的N ...

  8. android api 中文 (73)—— AdapterView

    前言 本章内容是android.widget.AdapterView,版本为Android 2.3 r1,翻译来自"cnmahj",欢迎大家访问他的博客:http://androi ...

  9. Android中文API (109) —— SimpleCursorTreeAdapter

    前言 本章内容是android.widget.SimpleCursorTreeAdapter,版本为Android 3.0 r1,翻译来自"深夜未眠",含示例代码,欢迎访问它的博客 ...

最新文章

  1. “12306” 是如何支撑百万 QPS 的?
  2. pgrouting进行路径规划之入门二
  3. TADOQuery parameter对象被不正确地定义。提供了不一致或不完整的信息
  4. 面试请不要再问我Spring Cloud底层原理
  5. java socket编程聊天室_Java Socket通信之聊天室功能
  6. LeetCode 1426. 数元素(哈希set)
  7. Ubuntu中apt与apt-get命令的区别
  8. bzoj 3714: [PA2014]Kuglarz
  9. 期货品种产业链图表_农业
  10. Ubuntu 18.04安装codeblocks 17-12
  11. html上传视频文件前端显示,文件分片上传之前端文件分片
  12. netty 百度网盘 密码
  13. 键盘上的prtsc,scrlk,pause键作用
  14. html继承有哪几种方式,JS实现继承的几种常用方式示例
  15. python做词云的图片素材_还在为专栏封面发愁?我用Python写了个词云生成器!
  16. 葫芦娃用计算机,葫芦娃互通版电脑版
  17. 给轮廓画出矩形和圆形边界
  18. 猪猪侠的黑客学习路线
  19. 【shaderforge小实例】 轮廓内发光
  20. python05元组字典

热门文章

  1. 系列推荐 |《最强的 VLC 多媒体开发教程》
  2. vue 仿写微信公众号自定义菜单
  3. 移动开发之我见 ——移动WEB
  4. 沁恒CH341是一个USB总线的转接芯片
  5. 如何用python做比分网_python爬虫足球比分-yltg888
  6. ONSPlayer玩RPGMV没声音的解决方法
  7. Java进阶(七)Set系列集合、Map集合体系
  8. 经验 | 训练多任务学习(Multi-task Learning)方法总结
  9. 伯禹公益AI《动手学深度学习PyTorch版》Task 03 学习笔记
  10. 使用ImageJ测图片中物体的平均灰度值