[关闭]
@946898963 2017-07-31T19:19:37.000000Z 字数 5893 阅读 1467

线程未捕获异常的处理

Java多线程


线程未捕获异常处理

线程中抛出的异常并不能被catch到,如下:

  1. try{
  2. new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. int i=1/0;
  6. }
  7. }).start();
  8. }catch (Exception e){
  9. System.out.println("error:"+e.getMessage());
  10. }

Thread中的运行时异常并不能被catch捕获,System.out.println("error:"+e.getMessage())并没有执行。
如果我们想要对Thread中抛出的异常进行处理,我们可以给线程设置异常捕获处理器

  1. Thread thread =new Thread(new Runnable() {
  2. @Override
  3. public void run() {
  4. int i=1/0;
  5. }
  6. });
  7. thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  8. @Override
  9. public void uncaughtException(Thread t, Throwable e) {
  10. System.out.println("error:"+e.getMessage());
  11. }
  12. });
  13. thread.start();

线程中抛出的异常会被setUncaughtExceptionHandler方法设置的异常捕获处理器捕获, System.out.println("error:"+e.getMessage())获得执行。

我们可以给每个线程的设置具体的未捕获异常处理器,也可以给所有的线程设置默认异常处理器。通过

  1. Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  2. @Override
  3. public void uncaughtException(Thread t, Throwable e) {
  4. }
  5. });

可以给所有线程设置默认的处理器。给所有的线程设置了默认的处理器后,对于没有设置特定的处理器的线程,当线程中出现未捕获的异常的时候,会使用默认的处理器来处理线程中出现的未捕获的异常。

如果没有给线程设置特定的处理器,那么线程的处理器就是线程的ThreadGroup对象,我们通过Thread.setDefaultUncaughtExceptionHandler设置的异常处理器的uncaughtException方法会在ThreadGroup对象的uncaughtException方法中被调用,具体看下一小节源码分析。

未捕获异常处理流程代码分析

当线程中出现未捕获异常后JVM将调用Thread中的dispatchUncaughtException方法把异常传递给线程的未捕获异常处理器。

  1. /**
  2. * Dispatch an uncaught exception to the handler. This method is
  3. * intended to be called only by the JVM.
  4. */
  5. private void dispatchUncaughtException(Throwable e) {
  6. getUncaughtExceptionHandler().uncaughtException(this, e);
  7. }

dispatchUncaughtException这个方法只是提供给JVM调用的。

  1. /* Returns the handler invoked when this thread abruptly terminates
  2. * due to an uncaught exception. If this thread has not had an
  3. * uncaught exception handler explicitly set then this thread's
  4. * <tt>ThreadGroup</tt> object is returned, unless this thread
  5. * has terminated, in which case <tt>null</tt> is returned.
  6. * @since 1.5
  7. * @return the uncaught exception handler for this thread
  8. */
  9. public UncaughtExceptionHandler getUncaughtExceptionHandler() {
  10. return uncaughtExceptionHandler != null ?uncaughtExceptionHandler : group;
  11. }

从代码可以看出如果没有设置uncaughtExceptionHandler,将使用线程所在的线程组来处理这个未捕获异常。线程组ThreadGroup实现了UncaughtExceptionHandler,所以可以用来处理未捕获异常。ThreadGroup类定义:

  1. class ThreadGroup implements Thread.UncaughtExceptionHandler

ThreadGroup实现的uncaughtException如下:

  1. /**
  2. * Called by the Java Virtual Machine when a thread in this
  3. * thread group stops because of an uncaught exception, and the thread
  4. * does not have a specific {@link Thread.UncaughtExceptionHandler}
  5. * installed.
  6. * <p>
  7. * The <code>uncaughtException</code> method of
  8. * <code>ThreadGroup</code> does the following:
  9. * <ul>
  10. * <li>If this thread group has a parent thread group, the
  11. * <code>uncaughtException</code> method of that parent is called
  12. * with the same two arguments.
  13. * <li>Otherwise, this method checks to see if there is a
  14. * {@linkplain Thread#getDefaultUncaughtExceptionHandler default
  15. * uncaught exception handler} installed, and if so, its
  16. * <code>uncaughtException</code> method is called with the same
  17. * two arguments.
  18. * <li>Otherwise, this method determines if the <code>Throwable</code>
  19. * argument is an instance of {@link ThreadDeath}. If so, nothing
  20. * special is done. Otherwise, a message containing the
  21. * thread's name, as returned from the thread's {@link
  22. * Thread#getName getName} method, and a stack backtrace,
  23. * using the <code>Throwable</code>'s {@link
  24. * Throwable#printStackTrace printStackTrace} method, is
  25. * printed to the {@linkplain System#err standard error stream}.
  26. * </ul>
  27. * <p>
  28. * Applications can override this method in subclasses of
  29. * <code>ThreadGroup</code> to provide alternative handling of
  30. * uncaught exceptions.
  31. *
  32. * @param t the thread that is about to exit.
  33. * @param e the uncaught exception.
  34. * @since JDK1.0
  35. */
  36. public void uncaughtException(Thread t, Throwable e) {
  37. if (parent != null) {
  38. parent.uncaughtException(t, e);
  39. } else {
  40. Thread.UncaughtExceptionHandler ueh =
  41. Thread.getDefaultUncaughtExceptionHandler();
  42. if (ueh != null) {
  43. ueh.uncaughtException(t, e);
  44. } else if (!(e instanceof ThreadDeath)) {
  45. System.err.print("Exception in thread \""
  46. + t.getName() + "\" ");
  47. e.printStackTrace(System.err);
  48. }
  49. }
  50. }

线程组处理未捕获异常,首先将异常消息通知给父线程组;如果父线程组为空,尝试利用一个默认的defaultUncaughtExceptionHandler获得异常处理器来处理异常,Thread.getDefaultUncaughtExceptionHandler()获得的异常捕获处理器就是我们之前通过Thread.getDefaultUncaughtExceptionHandler()设置的处理器;如果没有默认的异常处理器则将错误信息输出到System.err。

如果不给线程设置线程组,仍然可以正常的使用默认异常处理器。所有的线程都会有线程组,即使我们在构造线程时显示的传入null值的线程组,最终JVM也会为我们分配到一个线程组。Thread的init方法决定了这个特性:

  1. if (g == null) {
  2. /* Determine if it's an applet or not */
  3. /* If there is a security manager, ask the security manager
  4. what to do. */
  5. if (security != null) {
  6. g = security.getThreadGroup();
  7. }
  8. /* If the security doesn't have a strong opinion of the matter
  9. use the parent thread group. */
  10. if (g == null) {
  11. g = parent.getThreadGroup();
  12. }
  13. }

几个小例子

  1. Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  2. @Override
  3. public void uncaughtException(Thread t, Throwable e) {
  4. System.out.println("I catch a exception from " + Thread.currentThread().getName() + ":" + Thread.currentThread().getThreadGroup().getName());
  5. }
  6. });
  7. ThreadGroup myGroup = new ThredGroup("ThreadGroup");
  8. new Thread(myGroup, new Runnable() {
  9. @Override
  10. public void run() {
  11. int i = 1/0;
  12. }
  13. }, "thread1").start();
  14. new Thread(myGroup, new Runnable() {
  15. @Override
  16. public void run() {
  17. int i = 1/0;
  18. }
  19. }, "thread2").start();

打印结果如下:

  1. I catch a exception from thread1:myGroup
  2. I catch a exception from thread2:myGroup

通过自定义ThreadGrop改变上述机制。

  1. class OtherGroup extends ThreadGroup{
  2. public OtherGroup(String name) {
  3. super(name);
  4. }
  5. @Override
  6. public void uncaughtException(Thread t, Throwable e) {
  7. System.out.println("I am a other group and do nothing");
  8. }
  9. }
  10. Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  11. @Override
  12. public void uncaughtException(Thread t, Throwable e) {
  13. System.out.println("I catch a exception from " + Thread.currentThread().getName() + ":" + Thread.currentThread().getThreadGroup().getName());
  14. }
  15. });
  16. ThreadGroup myGroup = new OtherGroup("OtherGroup");
  17. new Thread(myGroup, new Runnable() {
  18. @Override
  19. public void run() {
  20. int i = 1/0;
  21. }
  22. }, "thread1").start();
  23. new Thread(myGroup, new Runnable() {
  24. @Override
  25. public void run() {
  26. int i = 1/0;
  27. }
  28. }, "thread2").start();

打印结果如下:

  1. I am a other group and do nothing
  2. I am a other group and do nothing

如果在OtherGroup的uncaughtException加上如下代码:

  1. super.uncaughtException(t,e);

那么执行结果会是:

  1. I catch a exception from thread1:badGroup
  2. I am a bad group and do nothing
  3. I catch a exception from thread2:badGroup
  4. I am a bad group and do nothing

参考文献:
Java核心技术讲义
Java线程未捕获异常处理
《Java并发》:第五节 线程异常处理
Java多线程——<七>多线程的异常捕捉

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注