一、概述

  为什么要单独讲多线程的异常捕捉呢?先看个例子:

public class ThreadException implements Runnable{
@Override
public void run() {
throw new RuntimeException();
}
//现象:控制台打印出异常信息,并运行一段时间后才停止
public static void main(String[] args){
//就算把线程的执行语句放到try-catch块中也无济于事
try{
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ThreadException());
}catch(RuntimeException e){
System.out.println("Exception has been handled!");
}
}
}

  在run中手动抛出了一个运行时异常,在main中启动线程,catch语句块中捕捉下异常,捕捉到打印一句话。运行结果如下图:

  发现异常被抛到了控制台,没有打印catch块中的语句。

  结论:多线程运行不能按照顺序执行过程中捕获异常的方式来处理异常,异常会被直接抛出到控制台(由于线程的本质,使得你不能捕获从线程中逃逸的异常。一旦异常逃逸出任务的run方法,它就会向外传播到控制台,除非你采用特殊的形式捕获这种异常。),这样会让你很头疼,无法捕捉到异常就无法处理异常而引发的问题。

  于是,我们一定会想如何在多线程中捕捉异常呢?

二、多线程中捕捉异常

  我们来按照下面的步骤完成这次实验:

  1.定义异常处理器

   要求,实现 Thread.UncaughtExceptionHandler的uncaughtException方法,如下:

/*
* 第一步:定义符合线程异常处理器规范的“异常处理器”
* 实现Thread.UncaughtExceptionHandler规范
*/
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
/*
* Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("caught "+e);
}
}

  2.定义使用该异常处理器的线程工厂

/*
* 第二步:定义线程工厂
* 线程工厂用来将任务附着给线程,并给该线程绑定一个异常处理器
*/
class HanlderThreadFactory implements ThreadFactory{
@Override
public Thread newThread(Runnable r) {
System.out.println(this+"creating new Thread");
Thread t = new Thread(r);
System.out.println("created "+t);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());//设定线程工厂的异常处理器
System.out.println("eh="+t.getUncaughtExceptionHandler());
return t;
}
}

  3.定义一个任务,让其抛出一个异常

/*
* 第三步:我们的任务可能会抛出异常
* 显示的抛出一个exception
*/
class ExceptionThread implements Runnable{
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("run() by "+t);
System.out.println("eh = "+t.getUncaughtExceptionHandler());
throw new RuntimeException();
}
}

  4.调用实验

/*
* 第四步:使用线程工厂创建线程池,并调用其execute方法
*/
public class ThreadExceptionUncaughtExceptionHandler{
public static void main(String[] args){
ExecutorService exec = Executors.newCachedThreadPool(new HanlderThreadFactory());
exec.execute(new ExceptionThread());
}
}

  运行结果如下图:

三、结论

  在java中要捕捉多线程产生的异常,需要自定义异常处理器,并设定到对应的线程工厂中(即第一步和第二步)。

四、拓展

  如果你知道将要在代码中处处使用相同的异常处理器,那么更简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的未捕获处理器。

这个处理器只有在不存在线程专有的未捕获异常处理器的情况下才会被调用。

public static void main(String[] args){
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
ExecutorService exec =Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}

注:以上代码均来自《thinking in java》,内容均是自己总结,如有错误,欢迎大家批评指正

Java多线程——<七>多线程的异常捕捉的更多相关文章

  1. JAVA并发七(多线程环境中安全使用集合API)

    在集合API中,最初设计的Vector和Hashtable是多线程安全的.例如:对于Vector来说,用来添加和删除元素的方法是同步的.如果只有一个线程与Vector的实例交互,那么,要求获取和释放对 ...

  2. Java多线程——<八>多线程其他概念

    一.概述 到第八节,就把多线程基本的概念都说完了.把前面的所有文章加连接在此: Java多线程——<一>概述.定义任务 Java多线程——<二>将任务交给线程,线程声明及启动 ...

  3. 七. 多线程编程2.Java线程模型

    Java运行系统在很多方面依赖于线程,所有的类库设计都考虑到多线程.实际上,Java使用线程来使整个环境异步.这有利于通过防止CPU循环的浪费来减少无效部分. 为更好的理解多线程环境的优势可以将它与它 ...

  4. Java线程和多线程(四)——主线程中的异常

    作为Java的开发者,在运行程序的时候会碰到主线程抛异常的情况.如果开发者使用Java的IDE比如Eclipse或者Intellij IDEA的话,可能是不需要直接面对这个问提的,因为IDE会处理运行 ...

  5. Java中的 多线程编程

    Java 中的多线程编程 一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序 ...

  6. 关于java基础、多线程、JavaWeb基础、数据库、SSM、Springboot技术汇总

    作者 : Stanley 罗昊 本人自行总结,纯手打,有疑问请在评论区留言 [转载请注明出处和署名,谢谢!] 一.java基础 1.多态有哪些体现形式? 重写.重载 2. Overriding的是什么 ...

  7. Java基础技术多线程与并发面试【笔记】

    Java基础技术多线程与并发 什么是线程死锁? ​死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,我们就可以称 ...

  8. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

  9. Java线程与多线程教程

    本文由 ImportNew - liken 翻译自 Journaldev.   Java线程是执行某些任务的轻量级进程.Java通过Thread类提供多线程支持,应用可以创建并发执行的多个线程. 应用 ...

随机推荐

  1. POI中设置Excel单元格格式样式(居中,字体,边框等)

    创建sheet什么的就不多说了,直接进入正题 HSSFCellStyle cellStyle = wb.createCellStyle();   一.设置背景色: cellStyle.setFillF ...

  2. 各种数据处理方案(SQL,NoSQL,其他)的应用场景

    综合stackoverflow和linkin上的相关讨论,还有我个人的工作经验:   Redis应用场景(大部分场景下memcache可以用Redis代替,所以不单独讨论) 线上业务,读写的高性能要求 ...

  3. mac里边配置android开发环境,intellij开发工具:

    1 在android的官网下载 android sdk的mac版 http://developer.android.com/sdk/index.html  选择mac的版本 下载后打开sdk-mana ...

  4. mysql 排重查询

    GROUP BY 语句可以实现某一列的去重查询. 直接上语句: select io_dev_id from io_info where (TID=1 AND host_name='yang1') GR ...

  5. BT9034: 仅 IE 和 Opera 支持 HTMLFrameElement 和 HTMLIFrameElement 的 document 属性

    标准参考 根据 DOM-2 中的描述,HTMLFrameElement 和 HTMLIFrameElement 都没有 'document' 属性. 关于 HTMLFrameElement 对象的详细 ...

  6. 【easyui】--普通js中获取easyui中分页信息(page,pageSize等)

    对于datagrid,获取其分页信息: 方法: var pageopt = $('#list_data').datagrid('getPager').data("pagination&quo ...

  7. GUID,UUID

    <? class System { function currentTimeMillis() { list($usec, $sec) = explode(" ",microt ...

  8. 从0 开始 WPF MVVM 企业级框架实现与说明 ---- 第四讲 WPF中 ControlTemplate

    上讲我们介绍了DataTemplate,现在我们就介绍下ControlTemplate,可能后面大多在编码时候会出现一些英文,工作习惯,请见谅. ControlTemplate: 控件的外观,也就是控 ...

  9. Go append方法

    append用来将元素添加到切片末尾并返回结果.看代码: package main import "fmt" func main() { x := [],,} y := [],,} ...

  10. yum代理设置

    vi /etc/yum.conf 加入以下:proxy=http://代理服务器ip:port 如果代理需要账号密码:proxy_username=userproxy_password=密码