多线程情况下如何捕获线程中的异常?
来源:互联网 发布:张艺谋奥运会知乎 编辑:程序博客网 时间:2024/05/02 04:17
摘要: 本文主要介绍使用UncaughtExceptionHandler,该如何正确的捕捉线程中的异常。
- Thread类最佳实践:
- 写的时候最好要设置线程名称 Thread.name,并设置线程组 ThreadGroup,目的是方便管理。在出现问题的时候,打印线程栈 (jstack -pid) 一眼就可以看出是哪个线程出的问题,这个线程是干什么的。
- 二种实现方式
方法一 Thread方式通过线程组,线程名,并设置UncaughtExceptionHandler来捕获异常
public static void main(String[] args) { try{ Thread t =new Thread(new Runnable(){ @Override public void run() { int i = 10/0; System.out.println("run...."); } }); t.setUncaughtExceptionHandler( new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("catch 到了"); } }); t.start(); }catch(Exception e){ System.out.println("catch 不到"); } }
输出如下:
catch 到了
public static void main(String[] args) { try{ Thread t =new Thread(new Runnable(){ @Override public void run() { int i = 10/0; System.out.println("run...."); } }); t.start(); }catch(Exception e){ System.out.println("catch 不到"); } }
输出如下:
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero at com.credit.subject.traditional.ThreadLocalTest$1.run(ThreadLocalTest.java:12) at java.lang.Thread.run(Thread.java:722)即使不加Handler 也catch不到,说明线程不能用try,catch来获取线程中的异常。需要使用Handler。
方法二 使用ExecutorService来捕获线程
- 由于线程的本质特性,使得你不能捕获从线程中逃逸的异常。一旦异常逃出任务的run()方法它就会向外传播到控制台,除非你采取特殊的步骤捕获这种错误的异常。 - 在Java SE5之前,你可以使用线程组来捕捉这种异常,但是有了Java SE5,就可以用Executor来解决这个问题了。 - 下面的任务总是会抛出一个异常,该异常会传播到其run()方法的外部,并且main()展示了当你运行它时所发生的事情:
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ExceptionThread implements Runnable { public void run() { throw new RuntimeException(); } public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); service.execute(new ExceptionThread()); }}
输出如下:
Exception in thread "pool-1-thread-1" java.lang.RuntimeException at com.abc.thread.ExceptionThread.run(ExceptionThread.java:6) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745)
将main的主体放在try-catch语句块中也是没有作用的:
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ExceptionThread implements Runnable { public void run() { throw new RuntimeException(); } public static void main(String[] args) { try { ExecutorService service = Executors.newCachedThreadPool(); service.execute(new ExceptionThread()); } catch (RuntimeException e) { System.out.println("Catched Runtime Exception."); } }}
这将产生于前面示例相同的结果:未捕获的异常。
- 为了解决这个问题,我们要修改Executor产生线程的方式。Thread.UncaughtExceptionHandler是Java SE5中的新接口,它允许你在每个Thread对象上都附着一个异常处理器。
- Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用。为了使用它,我们创建了一个新类型的ThreadFactory,它将在每个新创建的Thread对象上附着一个Thread.UncaughtExceptionHandler。
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ThreadFactory;public class ExceptionThread2 implements Runnable { public void run() { throw new RuntimeException("NullPointer"); } public static void main(String[] args) { ThreadFactory tFactory = new MyThreadFactory(); ExecutorService service = Executors.newCachedThreadPool(tFactory); Runnable task = new ExceptionThread2(); service.execute(task); }}class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { // 处理从线程里抛出来的异常。 public void uncaughtException(Thread t, Throwable e) { System.out.println("Catched Throwable: " + e.getClass().getSimpleName() + ", " + e.getMessage()); }}class MyThreadFactory implements ThreadFactory { // 重新组织创建线程的方式 public Thread newThread(Runnable r) { Thread t = new Thread(r); // 为每一个线程都绑定一个异常处理器。 t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); System.out.println("Thread[" + t.getName() + "] created."); return t; }}
执行的结果如下:
可以看到,线程池中有2个线程,当一个线程发生异常时,该异常被捕捉了。
上面的示例使得你可以按照具体情况(在newThread()方法中使用if, case等语句)为每个线程逐个的设置处理器。如果你知道将要在代码中处处使用相同的异常处理器,那么更简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的处理器即可:
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class SettingDefaultHandler { public static void main(String[] args) { // 为线程设置默认的异常处理器。 Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new ExceptionThread2()); }}
这个处理器只有在不存在线程专有的未捕获异常处理器的情况下才会被调用。系统会检查线程专有版本,如果没有发现,则检查线程组是否有专有的uncaughtException()方法,如果也没有,才会调用defaultUncaughtExceptionHandler。
- 多线程情况下如何捕获线程中的异常?
- 多线程:Java 线程中的异常捕获
- 捕获线程中的异常
- Java多线程之捕获子线程中的异常
- Java多线程-捕获线程异常
- 如何捕获java线程中的逃逸的异常
- 如何捕获java线程中的逃逸的异常
- 如何捕获java线程中的逃逸的异常
- 如何捕获java线程中的逃逸的异常
- 如何捕获子线程异常
- Java如何捕获线程异常?
- JAVA 线程中的异常捕获
- JAVA 线程中的异常捕获
- JAVA 线程中的异常捕获
- 【Java 多线程】Java中主线程如何捕获子线程抛出的异常
- 建议66:正确捕获多线程中的异常
- java基础-并发-如何捕获线程异常
- Java 多线程 捕获线程异常 Java编程思想读书笔记
- Spring Boot 入门
- ISO8583报文中的编、解码总结
- 【数据结构】-线性表-链表 熟练度max=3(不建立新节点,使链表L倒序)
- android7.1 保存图片到系统图库
- windows环境下编译skia
- 多线程情况下如何捕获线程中的异常?
- 抽象类(abstract)和接口(interface)的区别
- 利用Jenkins+Phantomas进行前端性能自动化测试
- 《凤凰项目》读书笔记一
- 内核控制 上下文切换、进入和退出临界区.....
- 与自己交流最难能可贵
- Android系统调用
- 有关UIViewController与UIVie
- Spring 开启Annotation <context:annotation-config> 和 <context:component-scan>诠释及区别