一.如何处理非正常的线程终止

我们都知道。当单线程的控制台程序由于发生了一个未捕获的异常而终止时,程序将停止运行,并产生与程序正常输出非常不同的栈追踪信息。
但是, 在并发程序中,单个线程的故障很有可能不会影响到整体,而这时的控制台中可能会输出栈追踪信息,但是没人看去看它,就相当于这个异常被忽略!!

但是,我们有应对的方法嘿嘿嘿
导致线程提前死亡的最主要的原因其实就是RuntimeException. 因为一般的RuntimeException是不可捕获的,它们不会再调用栈中逐层传递,而是默认地在控制台中输出栈追踪信息,并终止线程,线程非正常退出地后果可能是良性的,也可能是恶性的。

注意:任何代码都可能会抛出RuntimeException。每当调用一个方法时,都要对它的行为保持怀疑,不要盲目地认为它一定会正常返回,或者一定会抛出在方法头中声明的已检查类型。 对调用的代码越不熟悉,就越应该对其代码行为保持怀疑。

所以我们有下面的应对策略:
1)如果任务抛出了一个未检查异常,那么它将使线程终结,但它会首先通知框架该线程已经终结,然后框架可能会用新的线程来代替这个工作线程。

public void run(){
  Throwable thrown = null;
  try{
    while(!isInterrupted())
       runTask(getTaskFromWorkQueue());
  }catch(Throwable e){
    thrown = e;
  }finally{
    threadExited(this,thrown);//通知框架
  }
}

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

当一个线程由于未捕获异常而退出时,JVM会把这个事件报告给应用程序提供的UncaughtExceptionHandler异常处理器,如果没有提供任何异常处理器,那么默认的行为是将栈追踪信息输出到System.err.

public interface UncaughtExceptionHandler{
  void uncaughtException(Thread t, Throwable e);
}

而异常处理器如何处理未捕获的异常,这取决于对服务的质量的需求,最常见的响应方式是将一个错误信息以及相应的栈追踪信息写入应用程序日志中:

public class UEHLogger implements Thread.UncaughtExceptionHandler{
   public void uncaughtException(Thread t , Throwable e){
       Logger logger = Logger.getAnonymousLogger();
       logger.log(Level.SEVERE,"Thread terminated with exception: "+ t.getName().e);
   }
}

注意:在运行时间较长的应用程序中,通常会为所有线程的未捕获异常指定同一个异常处理器,并且该处理器至少将异常信息记录到日志中。

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