Java线程未捕获异常处理



线程执行完run方法即可正常退出,如果发生了没有捕获的异常则会异常退出。大多数情况下,由于异常导致的线程退出都不是我们想要的。所以在编写的代码的时候要尽可能的捕获处理可以处理的异常,但是也不能光简单的捕获异常然后什么也不做。下面介绍线程异常的处理办法。





   JVM为我们提供了线程的未捕获异常处理机制,通过Thread的setUncaughtExceptionHandler方法:




         

public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)

   


   可以设置当前线程的未捕获异常处理器。如下面的例子就通过设置uncaughtExceptionHandler成功捕获到了除0异常:

[java]  
    view plain 
     copy 
    
 
    
 
  
1. public static void main(String[] args) throws InterruptedException {                  
2.       
3. new Thread(new UncaughtException.Run());  
4. new Thread.UncaughtExceptionHandler() {  
5.           
6. @Override  
7. public void uncaughtException(Thread t, Throwable e) {  
8. "uncaughtExceptionHandler catch a Exception---------");  
9.             System.out.println(e.getMessage());  
10.         }  
11.     });  
12.       
13.     t.start();  
14. 100);  
15. }  
16.   
17. static class Run implements Runnable{  
18. @Override  
19. public void run() {  
20. "runnable run---------------");        
21. int i = 1/0;  
22.     }  
23. }  
 
 
 
 
    [java]  
    view plain 
     copy 
    
 
    
 
  
1. runnable run---------------  
2. uncaughtExceptionHandler catch a Exception---------  
3. / by zero


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


[java]  
    view plain 
     copy 
    
 
    
 
  
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. this, e);  
7. }  
 
 

 
 
 
 
    [java]  
    view plain 
     copy 
    
 
    
 
  
1. public UncaughtExceptionHandler getUncaughtExceptionHandler() {  
2. return uncaughtExceptionHandler != null ?  
3.         uncaughtExceptionHandler : group;  
4. }


   可见,

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

[java]  
    view plain 
     copy 
    
 
    
 
  
1. class ThreadGroup implements Thread.UncaughtExceptionHandler  
 
 
    ThreadGroup实现的uncaughtException如下: 
 
 
 
 
    [java]  
    view plain 
     copy 
    
 
    
 
  
1. public void uncaughtException(Thread t, Throwable e) {  
2. if (parent != null) {  
3.         parent.uncaughtException(t, e);  
4. else {  
5.         Thread.UncaughtExceptionHandler ueh =  
6.             Thread.getDefaultUncaughtExceptionHandler();  
7. if (ueh != null) {  
8.             ueh.uncaughtException(t, e);  
9. else if (!(e instanceof ThreadDeath)) {  
10. "Exception in thread \""  
11. "\" ");  
12.             e.printStackTrace(System.err);  
13.         }  
14.     }  
15. }

   


     设置了默认的异常处理器后,系统中所有未直接设置异常处理器的线程将使用这个默认的异常处理器。

[java]  
    view plain 
     copy 
    
 
    
 
  
1. public void defaultWay(){  
2. new Thread.UncaughtExceptionHandler() {  
3. @Override  
4. public void uncaughtException(Thread t, Throwable e) {  
5. "I catch a exception from  " + Thread.currentThread().getName() + ":" + Thread.currentThread().getThreadGroup().getName());  
6.         }  
7.     });  
8.   
9. new ThreadGroup("myGroup");  
10. new Thread(myGroup, new Runnable() {  
11. @Override  
12. public void run() {  
13. int i = 1/0;  
14.                 }  
15. "thread1").start();  
16.   
17. new Thread(myGroup, new Runnable() {  
18. @Override  
19. public void run() {  
20. int i = 1/0;  
21.         }  
22. "thread2").start();  
23.   
24. }

[java]  
    view plain 
     copy 
    
 
    
 
  
1. I catch a exception from  thread1:myGroup  
2. I catch a exception from  thread2:myGroup

 

    当然,出现上面的结果是因为使用了默认的ThreadGroup,我们可以破坏它这个机制。如果把上面代码中的ThreadGroup换成下面的BadGroup则情况会发生变化:

[java]  
    view plain 
     copy 
    
 
    
 
  
1. class BadGroup extends ThreadGroup{  
2. public BadGroup(String name) {  
3. super(name);  
4.     }  
5.   
6. @Override  
7. public void uncaughtException(Thread t, Throwable e) {  
8. "I am a bad group and do nothing");  
9.     }  
10. }



   上面的例子中,不论是ThreadGroup或者BadGroup都主动的给线程设置了线程组,那么如果不给线程设置线程组会怎么样呢?还会正常的使用默认异常处理器吗?这些跟线程组的来源相关,先看一个例子:


[java]  
    view plain 
     copy 
    
 
    
 
  
1. public void mainGroup() throws InterruptedException {  
2. new Thread.UncaughtExceptionHandler() {  
3. @Override  
4. public void uncaughtException(Thread t, Throwable e) {  
5. "I catch a exception from  " + Thread.currentThread().getName() + ":" + Thread.currentThread().getThreadGroup().getName());  
6.         }  
7.     });  
8. new Thread(new Runnable() {  
9. @Override  
10. public void run() {  
11. " said my thread group is " + Thread.currentThread().getThreadGroup().getName());  
12. new Thread(new Runnable() {  
13. @Override  
14. public void run() {  
15. " said my thread group is " + Thread.currentThread().getThreadGroup().getName());  
16. int i = 1/0;  
17.                  }  
18. "thread2").start();  
19.         }  
20. "thread1").start();  
21.   
22. 10);  
23. }

     这个方法中,首先创建启动了一个线程,然后又在这个线程中创建启动了另一个线程,它们都没有主动设置线程组。然而,它们都是有线程组的,其执行的结果如下:


[java]  
    view plain 
     copy 
    
 
    
 
  
1. thread1 said my thread group is main  
2. thread2 said my thread group is main  
3. I catch a exception from  thread2:main

    虚拟机执行main方法的线程属于一个名字叫做“main”的线程组。在应用程序中,创建一个线程的时候如果没有重新制定线程组,则会继承这个“main”线程组。也就是说

所有的线程都会有线程组,即使我们在构造线程时显示的传入null值的线程组,最终JVM也会为我们分配到一个线程组。Thread的init方法决定了这个特性:

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