前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止

JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止

作者头像
bear_fish
发布2018-09-19 16:12:36
8920
发布2018-09-19 16:12:36
举报
文章被收录于专栏:用户2442861的专栏

http://blog.csdn.net/u013256816/article/details/50417822

当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境中,线程抛出的异常是不能用try....catch捕获的,这样就有可能导致一些问题的出现,比如异常的时候无法回收一些系统资源,或者没有关闭当前的连接等等。

首先来看一个示例:

[java] view plain copy

  1. package com.exception;  
  2. public class NoCaughtThread  
  3. {  
  4. public static void main(String[] args)  
  5.     {  
  6. try
  7.         {  
  8.             Thread thread = new Thread(new Task());  
  9.             thread.start();  
  10.         }  
  11. catch (Exception e)  
  12.         {  
  13.             System.out.println("==Exception: "+e.getMessage());  
  14.         }  
  15.     }  
  16. }  
  17. class Task implements Runnable  
  18. {  
  19. @Override
  20. public void run()  
  21.     {  
  22.         System.out.println(3/2);  
  23.         System.out.println(3/0);  
  24.         System.out.println(3/1);  
  25.     }  
  26. }  

运行结果:

[plain] view plain copy

  1. 1  
  2. Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero  
  3.     at com.exception.Task.run(NoCaughtThread.java:25)  
  4.     at java.lang.Thread.run(Unknown Source)  

可以看到在多线程中通过try....catch试图捕获线程的异常是不可取的。

Thread的run方法是不抛出任何检查型异常的,但是它自身却可能因为一个异常而被中止,导致这个线程的终结。 首先介绍一下如何在线程池内部构建一个工作者线程,如果任务抛出了一个未检查异常,那么它将使线程终结,但会首先通知框架该现场已经终结。然后框架可能会用新的线程来代替这个工作线程,也可能不会,因为线程池正在关闭,或者当前已有足够多的线程能满足需要。当编写一个向线程池提交任务的工作者类线程类时,或者调用不可信的外部代码时(例如动态加载的插件),使用这些方法中的某一种可以避免某个编写得糟糕的任务或插件不会影响调用它的整个线程。

[java] view plain copy

  1. package com.exception;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. public class InitiativeCaught  
  5. {  
  6. public void threadDeal(Runnable r, Throwable t)  
  7.     {  
  8.         System.out.println("==Exception: "+t.getMessage());  
  9.     }  
  10. class InitialtiveThread implements Runnable  
  11.     {  
  12. @Override
  13. public void run()  
  14.         {  
  15.             Throwable thrown = null;  
  16. try
  17.             {  
  18.                 System.out.println(3/2);  
  19.                 System.out.println(3/0);  
  20.                 System.out.println(3/1);  
  21.             }  
  22. catch(Throwable e)  
  23.             {  
  24.                 thrown =e;  
  25.             }  
  26. finally{  
  27.                 threadDeal(this,thrown);  
  28.             }  
  29.         }  
  30.     }  
  31. public static void main(String[] args)  
  32.     {  
  33.         ExecutorService exec = Executors.newCachedThreadPool();  
  34.         exec.execute(new InitiativeCaught().new InitialtiveThread());  
  35.         exec.shutdown();  
  36.     }  
  37. }  

运行结果:

[plain] view plain copy

  1. 1  
  2. ==Exception: / by zero  

上面介绍了一种主动方法来解决未检测异常。在Thread ApI中同样提供了UncaughtExceptionHandle,它能检测出某个由于未捕获的异常而终结的情况。这两种方法是互补的,通过将二者结合在一起,就能有效地防止线程泄露问题。

如下:

[java] view plain copy

  1. package com.exception;  
  2. import java.lang.Thread.UncaughtExceptionHandler;  
  3. public class WitchCaughtThread  
  4. {  
  5. public static void main(String args[])  
  6.     {  
  7.         Thread thread = new Thread(new Task());  
  8.         thread.setUncaughtExceptionHandler(new ExceptionHandler());  
  9.         thread.start();  
  10.     }  
  11. }  
  12. class ExceptionHandler implements UncaughtExceptionHandler  
  13. {  
  14. @Override
  15. public void uncaughtException(Thread t, Throwable e)  
  16.     {  
  17.         System.out.println("==Exception: "+e.getMessage());  
  18.     }  
  19. }  

运行结果:

[plain] view plain copy

  1. 1  
  2. ==Exception: / by zero  

同样可以为所有的Thread设置一个默认的UncaughtExceptionHandler,通过调用Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)方法,这是Thread的一个static方法。

如下:

[java] view plain copy

  1. package com.exception;  
  2. import java.lang.Thread.UncaughtExceptionHandler;  
  3. public class WitchCaughtThread  
  4. {  
  5. public static void main(String args[])  
  6.     {  
  7.         Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());  
  8.         Thread thread = new Thread(new Task());  
  9.         thread.start();  
  10.     }  
  11. }  
  12. class ExceptionHandler implements UncaughtExceptionHandler  
  13. {  
  14. @Override
  15. public void uncaughtException(Thread t, Throwable e)  
  16.     {  
  17.         System.out.println("==Exception: "+e.getMessage());  
  18.     }  
  19. }  

运行结果:

[plain] view plain copy

  1. 1  
  2. ==Exception: / by zero  

如果采用线程池通过execute的方法去捕获异常,先看下面的例子:

[java] view plain copy

  1. public class ExecuteCaught  
  2. {  
  3. public static void main(String[] args)  
  4.     {  
  5.         ExecutorService exec = Executors.newCachedThreadPool();  
  6.         Thread thread = new Thread(new Task());  
  7.         thread.setUncaughtExceptionHandler(new ExceptionHandler());  
  8.         exec.execute(thread);  
  9.         exec.shutdown();  
  10.     }  
  11. }  

ExceptionHandler可参考上面的例子,运行结果:

[plain] view plain copy

  1. 1  
  2. Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero  
  3.     at com.exception.Task.run(NoCaughtThread.java:25)  
  4.     at java.lang.Thread.run(Unknown Source)  
  5.     at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)  
  6.     at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)  
  7.     at java.lang.Thread.run(Unknown Source)  

可以看到并未捕获到异常。

这时需要将异常的捕获封装到Runnable或者Callable中,如下所示:

[java] view plain copy

  1. package com.exception;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. public class ExecuteCaught  
  5. {  
  6. public static void main(String[] args)  
  7.     {  
  8.         ExecutorService exec = Executors.newCachedThreadPool();  
  9.         exec.execute(new ThreadPoolTask());  
  10.         exec.shutdown();  
  11.     }  
  12. }  
  13. class ThreadPoolTask implements Runnable  
  14. {  
  15. @Override
  16. public void run()  
  17.     {  
  18.         Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler());  
  19.         System.out.println(3/2);  
  20.         System.out.println(3/0);  
  21.         System.out.println(3/1);  
  22.     }  
  23. }  

运行结果:

[plain] view plain copy

  1. 1  
  2. ==Exception: / by zero  

只有通过execute提交的任务,才能将它抛出的异常交给UncaughtExceptionHandler,而通过submit提交的任务,无论是抛出的未检测异常还是已检查异常,都将被认为是任务返回状态的一部分。如果一个由submit提交的任务由于抛出了异常而结束,那么这个异常将被Future.get封装在ExecutionException中重新抛出。 下面两个例子:

[java] view plain copy

  1. package com.exception;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. public class SubmitCaught  
  5. {  
  6. public static void main(String[] args)  
  7.     {  
  8.         ExecutorService exec = Executors.newCachedThreadPool();  
  9.         exec.submit(new Task());  
  10.         exec.shutdown();  
  11.     }  
  12. }  

[java] view plain copy

  1. package com.exception;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. public class SubmitCaught  
  5. {  
  6. public static void main(String[] args)  
  7.     {  
  8.         ExecutorService exec = Executors.newCachedThreadPool();  
  9.         exec.submit(new ThreadPoolTask());  
  10.         exec.shutdown();  
  11.     }  
  12. }  

运行结果都是:

[plain] view plain copy

  1. 1  

这样可以证实我的观点。接下来通过这个例子可以看到捕获的异常:

[java] view plain copy

  1. package com.exception;  
  2. import java.util.concurrent.ExecutionException;  
  3. import java.util.concurrent.ExecutorService;  
  4. import java.util.concurrent.Executors;  
  5. import java.util.concurrent.Future;  
  6. public class SubmitCaught  
  7. {  
  8. public static void main(String[] args)  
  9.     {  
  10.         ExecutorService exec = Executors.newCachedThreadPool();  
  11.         Future<?> future = exec.submit(new Task());  
  12.         exec.shutdown();  
  13. try
  14.         {  
  15.             future.get();  
  16.         }  
  17. catch (InterruptedException | ExecutionException e)  
  18.         {  
  19.             System.out.println("==Exception: "+e.getMessage());  
  20.         }  
  21.     }  
  22. }  

运行结果:

[plain] view plain copy

  1. 1  
  2. ==Exception: java.lang.ArithmeticException: / by zero  

希望我整理的这些能够给各位有所帮助。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年08月11日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档