您的位置:首页 > 编程语言 > Java开发

如何捕获java线程中的逃逸的异常

2016-05-18 11:43 441 查看
更多的相关的文章:http://www.cnblogs.com/lao-liang/p/5056176.html;(解释了java中异常的一些分类);http://www.cnblogs.com/freeliver54/archive/2011/10/17/2215423.html;(解释了如何捕获线程中出现的异常)

前言:由于线程中的本质特性,使得你不能捕获从线程中逃逸的异常。一旦异常逃出任务的run()方法,它就会向外传播,它就会向外传播到控制台,除非你采取特殊的步骤来捕获这种错误的异常。在javaSE5之前,你可以使用线程组来捕获这些异常。但是有了java SE5,就可以使用Executor来解决这个问题。因而你不需要了解有关线程组的任何的只是了。

   例如在下面的例子中,就会由于在run方法中,抛出了异常,却无法捕获,从而导致了线程的停止执行。

 

总的来说,在java线程中,在run方法中,我们要在run()方法中,把一切的异常有处理掉,也就try-catch掉。不能让这个线程抛出异常,因为如果我们不使用特殊的方式的话,我们是无法捕获从这个线程中逃逸的异常的。异常一旦抛出了,那么这个线程就会停止运行,但是不会影响主线程和其它的线程。因为主线程和其它的线程都不知道它抛出了异常。

public class ExceptionThread implements  Runnable {
@Override
public void run() {
throw new RuntimeException();
}

public static void main(String[] args) {
Thread thread = new Thread(new ExceptionThread());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(thread);

}
}

运行的结果为:

Exception in thread "pool-1-thread-1" java.lang.RuntimeException
at Concurrency.ExceptionThread.run(ExceptionThread.java:9)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

即使将main的主体放到try-catch语句块中也是没有用的。一样会出现未捕获的异常。

public static void main(String[] args) {
try {
Thread thread = new Thread(new ExceptionThread());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(thread);
} catch (Exception e) {
e.printStackTrace();
}
}
为了解决这个问题,我们要修改Executor中产生线程的方式。Thread.UnCaughtExecptionHandler是javaSE5中的新接口,它允许你为每一个Thread对象都附上一个异常处理器。比如像以下的这个例子:
class ExceptionThread2 implements  Runnable{
@Override
public void run() {
Thread t= Thread.currentThread();
System.out.println("run() by" + t);
System.out.println("eh=" + t.getUncaughtExceptionHandler());
throw new RuntimeException();
}
}

class MyUncaughtExceptionHandler implements  Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread thread, Throwable ex) {
System.out.println("caught" + ex);
}
}
//调用工厂化方法为每一个thread对象都附上一个未捕获异常的处理器。
class HandlerThreadFactory implements ThreadFactory{
@Override
public Thread newThread(Runnable r) {
System.out.println(this + "creating new Thread");
Thread t = new Thread(r);
System.out.println("created " + t);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println("eh=" + t.getUncaughtExceptionHandler());
return t;
}
}
public class CaptureUncaughtException {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
exec.execute(new ExceptionThread2());

}
}
运行的结果为:
Concurrency.HandlerThreadFactory@7852e922 creating new Thread
created Thread[Thread-0,5,main]
eh=Concurrency.MyUncaughtExceptionHandler@4e25154f
run() byThread[Thread-0,5,main]
eh=Concurrency.MyUncaughtExceptionHandler@4e25154f
Concurrency.HandlerThreadFactory@7852e922 creating new Thread
created Thread[Thread-1,5,main]
eh=Concurrency.MyUncaughtExceptionHandler@626007aa
caughtjava.lang.RuntimeException
上面的示例使得你可以按照具体的情况逐个地设置处理器。如果你知道将要在代码中处处使用相同的异常处理器,那么更加简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的未捕获异常的处理器。比如像下面的例子:
public class SettingDefaultHandler{
public static void main(String[] args){
Thread.setDefaultUncaughtException(new MyUncaughtExceptionHandler());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}
}
说明:这个处理器只有在不存在线程专有的未捕获异常的情况下才会被调用。系统会检查线程的专有的版本,如果没有发现,则检查线程组是有有其专有的uncaughtException()方法。如果也没有,在调用defaultUncaughtExcept
   在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked exception处理掉。这一点是通过java.lang.Runnable.run()方法声明(因为此方法声明上没有throw exception部分)进行了约束。但是线程依然有可能抛出unchecked exception(如运行时异常),当此类异常跑抛出时,线程就会终结,而对于主线程和其他线程完全不受影响,且完全感知不到某个线程抛出的异常(也是说完全无法catch到这个异常)。JVM的这种设计源自于这样一种理念:“线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。”基于这样的设计理念,在Java中,线程方法的异常(无论是checked还是unchecked exception),都应该在线程代码边界之内(run方法内)进行try catch并处理掉.换句话说,我们不能捕获从线程中逃逸的异常。
看下面的例子:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExceptionThread implements Runnable {

@Override
public void run() {
throw new RuntimeException("这个线程就干了这么一件事,抛出一个运行时异常");
}

public static void main(String[] args) {
try {
ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new ExceptionThread());
System.out.println("该干嘛干嘛去");
} catch (RuntimeException e) {
System.out.println("能不能捕获到异常?");
}

}

}

运行结果:
该干嘛干嘛去
Exception in thread "pool-1-thread-1" java.lang.RuntimeException: 这个线程就干了这么一件事,抛出一个运行时异常
at ExceptionThread.run(ExceptionThread.java:8)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
从运行结果中,我们可以看到的是,这个异常在main线程中没有catch到,即 System.out.println("能不能捕获到异常?");   永远不会执行到。        问题来了,我们如果需要捕获其线程的unchecked异常时该怎么办?Java SE5之后,我们可以通过Executor来解决这个我问题。为了解决这个问题,我们需要修改Executor产生线程的方式。Thread.UncaughtExceptionHandler是java SE5中的新接口,它允许我们在每一个Thread对象上添加一个异常处理器。(UncaughtExceptionHandler)。Thread.UncaughtExceptionHandler.uncaughtException()方法会在线程因未捕获的异常而面临死亡时被调用。下面这个例子简单的演示了如何使用UncaughtExceptionHandler
 
import java.lang.Thread.UncaughtExceptionHandler;

/**
* 用于捕获异常---捕获的是uncheckedException
*
* @author February30th
*
*/
public class MyUnchecckedExceptionhandler implements UncaughtExceptionHandler {

@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获到异常:" + e);
}

}

import java.util.concurrent.ThreadFactory;

public class HandlerThreadFactory implements ThreadFactory {

@Override
public Thread newThread(Runnable r) {
System.out.println("创建一个新的线程");
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
System.out.println("eh121 = " + t.getUncaughtExceptionHandler());
return t;
}

}

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadExceptionTest {

/**
* @param args
*/
public static void main(String[] args) {
//下面有3中方式来执行线程。
//第1种按照普通的方式。这是能捕获到异常
Thread t = new Thread(new ExceptionThread2());
t.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
t.start();
//第2种按照现成池,直接按照thread方式,此时不能捕获到异常,为什么呢?因为在下面代码中创建了一个线程,且设置了异常处理器,
//但是呢,在我们线程池中会重设置新的Thread对象,而这个Thread对象没有设置任何异常处理器,换句话说,我们在线程池外对线程做的
//任何操作都是没有用的
ExecutorService exec1 = Executors.newCachedThreadPool();
Runnable runnable = new ExceptionThread2();
Thread t1 = new Thread(runnable);
t1.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
exec1.execute(runnable);

//第3种情况一样的,也是走的线程池,但是呢是通过ThreadFactory方式,在ThreadFactory中会对线程做一些控制,可以设置异常处理器
//此时是可以捕获异常的。
ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
exec.execute(new ExceptionThread2());

}

}

运行结果
创建一个新的线程
eh121 = MyUnchecckedExceptionhandler@1b8e059
run() byThread[Thread-0,5,main]
eh=MyUnchecckedExceptionhandler@1b8e059
捕获到异常:java.lang.RuntimeException: 抛出运行时异常
从上述的运行结果中可以看到,未捕获的异常是通过uncaughtException来捕获的。按照上述的实例,我们可以按照具体的情况,逐个地设置处理器。但是如果我们知道将要在代码的所有地方都是用相同的异常处理器,那么更简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的未捕获异常处理器。看下面的例子:
Thread.setDefaultUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
exec.execute(new ExceptionThread2());
这个默认的处理器只有在线程不存在非默认的异常处理器时才会调用。 在运行时,系统会检查线程是否有属于自己的异常处理器,如果发现没有,就去检查相应的线程组是否有专有的异常处理器,如果发现也没有,再调用默认的异常处理器。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: