java线程异常处理

前言

    想讨论这个话题有一段时间了。记得几年前的时候去面试,有人就问过我一个相似的问题。就是java thread中对于异常的处理状况。因为java thread自己牵涉到并发、锁等相关的问题已经够复杂了。再加上异常处理这些东西,使得它更加特殊。 归纳起来,不外乎是三个主要的问题。1. 在java启动的线程里能够抛出异常吗? 2. 在启动的线程里能够捕捉异常吗? 3. 若是能够捕捉异常,对于checked exception和unchecked exception,他们分别有什么的处理方式呢? java

     如今, 咱们就一个个的来讨论。 面试

线程里抛出异常

    咱们能够尝试一下在线程里抛异常。按照咱们的理解,假定咱们要在某个方法里抛异常,须要在该定义的方法头也加上声明。那么一个最简单的方式可能以下: 网络

Java代码   收藏代码
  1. public class Task implements Runnable {  
  2.   
  3.     @Override  
  4.     public void run() throws Exception {  
  5.         int number0 = Integer.parseInt("1");  
  6.         throw new Exception("Just for test");  
  7.     }  
  8. }  

    但是,若是咱们去编译上面这段代码,会发现根本就编译不过去的。系统报的错误是: 并发

Java代码   收藏代码
  1. Task.java:3: error: run() in Task cannot implement run() in Runnable  
  2.     public void run() throws Exception {  
  3.                 ^  
  4.   overridden method does not throw Exception  
  5. 1 error  

     由此咱们发现这种方式行不通。也就是说,在线程里直接抛异常是不行的。但是,这又会引出一个问题,若是咱们在线程代码里头确实是产生了异常,那该怎么办 呢?好比说,咱们经过一个线程访问一些文件或者对网络进行IO操做,结果产生了异常。或者说访问某些资源的时候系统崩溃了。这样的场景是确实可能会发生 的,咱们就须要针对这些状况进行进一步的讨论。 ide

异常处理的几种方式

     在前面提到的几种在线程访问资源产生了异常的状况。咱们能够看,好比说咱们访问文件系统的时候,会抛出IOException, FileNotFoundException等异常。咱们在访问的代码里其实是须要采用两种方式来处理的。一种是在使用改资源的方法头增长throws IOException, FileNotFoundException等异常的修饰。还有一种是直接在这部分的代码块增长try/catch部分。由前面咱们的讨论已经发现,在方 法声明加throws Exception的方式是行不通的。那么就只有使用try/catch这么一种方式了。 this

    另外,咱们也知道,在异常的处理上,通常异常能够分为checked exception和unchecked exception。做为unchecked exception,他们一般是指一些比较严重的系统错误或者系统设计错误,好比Error, OutOfMemoryError或者系统直接就崩溃了。对于这种异常发生的时候,咱们通常是无能为力也无法恢复的。那么这种状况的发生,咱们会怎么来处 理呢? spa

checked exception

     在线程里面处理checked exception,按照咱们之前的理解,咱们是能够直接捕捉它来处理的。在一些thread的示例里咱们也见过。好比说下面的一部分代码: 线程

Java代码   收藏代码
  1. import java.util.Date;  
  2. import java.util.concurrent.TimeUnit;  
  3.   
  4. public class FileLock implements Runnable {  
  5.     @Override  
  6.     public void run() {  
  7.         for(int i = 0; i < 10; i++) {  
  8.             System.out.printf("%s\n"new Date());  
  9.             try {  
  10.                 TimeUnit.SECONDS.sleep(1);  
  11.             } catch(InterruptedException e) {  
  12.                 System.out.printf("The FileClock has been interrupted");  
  13.             }  
  14.         }  
  15.     }  
  16. }  

     咱们定义了一个线程执行代码,而且在这里由于调用TimeUnit.SECONDS.sleep()方法而须要捕捉异常。由于这个方法自己就会抛出InterruptedException,咱们必需要用try/catch块来处理。 设计

     咱们启动该线程并和它交互的代码以下: orm

Java代码   收藏代码
  1. import java.util.concurrent.TimeUnit;  
  2.   
  3. public class Main {  
  4.     public static void main(String[] args) {  
  5.         // Creates a FileClock runnable object and a Thread  
  6.         // to run it  
  7.         FileClock clock=new FileClock();  
  8.         Thread thread=new Thread(clock);  
  9.           
  10.         // Starts the Thread  
  11.         thread.start();  
  12.         try {  
  13.             // Waits five seconds  
  14.             TimeUnit.SECONDS.sleep(5);  
  15.         } catch (InterruptedException e) {  
  16.             e.printStackTrace();  
  17.         };  
  18.         // Interrupts the Thread  
  19.         thread.interrupt();  
  20.     }  
  21. }  

     这部分的代码是启动FileLock线程并尝试去中断它。咱们能够发如今运行的时候FileLock里面执行的代码可以正常的处理异常。

    所以,在thread里面,若是要处理checked exception,简单的一个try/catch块就能够了。

unchecked exception

     对于这种unchecked exception,相对来讲就会不同一点。实际上,在Thread的定义里有一个实例方 法:setUncaughtExceptionHandler(UncaughtExceptionHandler). 这个方法能够用来处理一些unchecked exception。那么,这种状况的场景是如何的呢?

    setUncaughtExceptionHandler()方法至关于一个事件注册的入口。在jdk里面,该方法的定义以下:

Java代码   收藏代码
  1. public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {  
  2.     checkAccess();  
  3.     uncaughtExceptionHandler = eh;  
  4. }  

     而UncaughtExceptionHandler则是一个接口,它的声明以下:

Java代码   收藏代码
  1. public interface UncaughtExceptionHandler {  
  2.     /** 
  3.      * Method invoked when the given thread terminates due to the 
  4.      * given uncaught exception. 
  5.      * <p>Any exception thrown by this method will be ignored by the 
  6.      * Java Virtual Machine. 
  7.      * @param t the thread 
  8.      * @param e the exception 
  9.     */  
  10.     void uncaughtException(Thread t, Throwable e);  
  11. }  

     在异常发生的时候,咱们传入的UncaughtExceptionHandler参数的uncaughtException方法会被调用。

     综合前面的讨论,咱们这边要实现handle unchecked exception的方法的具体步骤能够总结以下:

1. 定义一个类实现UncaughtExceptionHandler接口。在实现的方法里包含对异常处理的逻辑和步骤。

2. 定义线程执行结构和逻辑。这一步和普通线程定义同样。

3. 在建立和执行改子线程的方法里在thread.start()语句前增长一个thread.setUncaughtExceptionHandler语句来实现处理逻辑的注册。

    下面,咱们就按照这里定义的步骤来实现一个示例:

    首先是实现UncaughtExceptionHandler接口部分:

Java代码   收藏代码
  1. import java.lang.Thread.UncaughtExceptionHandler;  
  2.   
  3. public class ExceptionHandler implements UncaughtExceptionHandler {  
  4.     public void uncaughtException(Thread t, Throwable e) {  
  5.         System.out.printf("An exception has been captured\n");  
  6.         System.out.printf("Thread: %s\n", t.getId());  
  7.         System.out.printf("Exception: %s: %s\n",   
  8.                 e.getClass().getName(), e.getMessage());  
  9.         System.out.printf("Stack Trace: \n");  
  10.         e.printStackTrace(System.out);  
  11.         System.out.printf("Thread status: %s\n", t.getState());  
  12.     }  
  13. }  

     这里咱们添加的异常处理逻辑很简单,只是把线程的信息和异常信息都打印出来。

    而后,咱们定义线程的内容,这里,咱们故意让该线程产生一个unchecked exception:

Java代码   收藏代码
  1. public class Task implements Runnable {  
  2.   
  3.     @Override  
  4.     public void run() {  
  5.         int number0 = Integer.parseInt("TTT");  
  6.     }  
  7. }  

     从这代码里咱们能够看到,Integer.parseInt()里面的参数是错误的,确定会抛出一个异常来。

    如今,咱们再把建立线程和注册处理逻辑的部分补上来:

Java代码   收藏代码
  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.         Task task = new Task();  
  4.         Thread thread = new Thread(task);  
  5.         thread.setUncaughtExceptionHandler(new ExceptionHandler());  
  6.         thread.start();  
  7.     }  
  8. }  

     如今咱们去执行整个程序,会发现有以下的结果:

Java代码   收藏代码
  1. An exception has been captured  
  2. Thread: 8  
  3. Exception: java.lang.NumberFormatException: For input string: "TTT"  
  4. Stack Trace:   
  5. java.lang.NumberFormatException: For input string: "TTT"  
  6.     at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)  
  7.     at java.lang.Integer.parseInt(Integer.java:492)  
  8.     at java.lang.Integer.parseInt(Integer.java:527)  
  9.     at Task.run(Task.java:5)  
  10.     at java.lang.Thread.run(Thread.java:722)  
  11. Thread status: RUNNABLE  

    这部分的输出正好就是咱们前面实现UncaughtExceptionHandler接口的定义。

    所以,对于unchecked exception,咱们也能够采用相似事件注册的机制作必定程度的处理。

总结

     Java thread里面关于异常的部分比较奇特。你不能直接在一个线程里去抛出异常。通常在线程里碰到checked exception,推荐的作法是采用try/catch块来处理。而对于unchecked exception,比较合理的方式是注册一个实现UncaughtExceptionHandler接口的对象实例来处理。这些细节的东西若是没有碰到 过确实很难回答。

相关文章
相关标签/搜索