捕获线程中的异常

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

下面的任务总是会抛出一个异常,该异常会传播到其run()方法的外部,并且main()展示了当你运行它时所发生的事情:

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

public class ExceptionThread implements Runnable {
    public void run() {
        throw new RuntimeException();
    }
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new ExceptionThread());
    }
}

输出如下:

Exception in thread "pool-1-thread-1" java.lang.RuntimeException
	at com.abc.thread.ExceptionThread.run(ExceptionThread.java:6)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)

将main的主体放在try-catch语句块中也是没有作用的:

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

public class ExceptionThread implements Runnable {
    public void run() {
        throw new RuntimeException();
    }
    public static void main(String[] args) {
        try {
            ExecutorService service = Executors.newCachedThreadPool();
            service.execute(new ExceptionThread());
        } catch (RuntimeException e) {
            System.out.println("Catched Runtime Exception.");
        }
    }
}

这将产生于前面示例相同的结果:未捕获的异常。

为了解决这个问题,我们要修改Executor产生线程的方式。Thread.UncaughtExceptionHandler是Java SE5中的新接口,它允许你在每个Thread对象上都附着一个异常处理器。Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用。为了使用它,我们创建了一个新类型的ThreadFactory,它将在每个新创建的Thread对象上附着一个Thread.UncaughtExceptionHandler。

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

public class ExceptionThread2 implements Runnable {
    public void run() {
        throw new RuntimeException("NullPointer");
    }
    
    public static void main(String[] args) {
        ThreadFactory tFactory = new MyThreadFactory();
        ExecutorService service = Executors.newCachedThreadPool(tFactory);
        Runnable task = new ExceptionThread2();
        service.execute(task);
    }
}

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    // 处理从线程里抛出来的异常。
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("Catched Throwable: " + 
                e.getClass().getSimpleName() + ", " + e.getMessage());
    }
}

class MyThreadFactory implements ThreadFactory {
    // 重新组织创建线程的方式
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        // 为每一个线程都绑定一个异常处理器。
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        System.out.println("Thread[" + t.getName() + "] created.");
        return t;
    }
}

执行的结果如下:

可以看到,线程池中有2个线程,当一个线程发生异常时,该异常被捕捉了。

上面的示例使得你可以按照具体情况(在newThread()方法中使用if, case等语句)为每个线程逐个的设置处理器。如果你知道将要在代码中处处使用相同的异常处理器,那么更简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的处理器即可:

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

public class SettingDefaultHandler {
    public static void main(String[] args) {
        // 为线程设置默认的异常处理器。
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread2());
    }
}

这个处理器只有在不存在线程专有的未捕获异常处理器的情况下才会被调用。系统会检查线程专有版本,如果没有发现,则检查线程组是否有专有的uncaughtException()方法,如果也没有,才会调用defaultUncaughtExceptionHandler。

时间: 2024-08-24 03:43:18

捕获线程中的异常的相关文章

多线程捕获线程中的异常

Thread类最佳实践: 写的时候最好要设置线程名称 Thread.name,并设置线程组 ThreadGroup,目的是方便管理.在出现问题的时候,打印线程栈 (jstack -pid) 一眼就可以看出是哪个线程出的问题,这个线程是干什么的. 二种实现方式 方法一 Thread方式通过线程组,线程名,并设置UncaughtExceptionHandler来捕获异常 public static void main(String[] args) { try{ Thread t =new Threa

编写高质量代码改善C#程序的157个建议——建议66:正确捕获多线程中的异常

建议66:正确捕获多线程中的异常 多线程的异常处理需要采用特殊的方式.一下这种方式会存在问题: try { Thread t = new Thread((ThreadStart)delegate { throw new Exception("多线程异常"); }); t.Start(); } catch (Exception error) { MessageBox.Show(error.Message + Environment.NewLine + error.StackTrace);

JAVA 线程中的异常捕获

在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked exception处理掉.这一点是通过java.lang.Runnable.run()方法声明(因为此方法声明上没有throw exception部分)进行了约束.但是线程依然有可能抛出unchecked exception(如运行时异常),当此类异常跑抛出时,线程就会终结,而对于主线程和其他线程完全

Java并发学习之八——在线程中处理不受控制的异常

本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.Java里有2种异常: 检查异常:这些异常必须强制捕获她们或在一个方法里的throws子句中. 未检查异常:这些异常不用强制捕获它们. 2.在一个线程对象的run()方法里抛出一个检查异常,我们必须捕获并处理她们.因为run()方法不接受throws子句.当一个非检查异常抛出,默认的的行为是在控制台写下stack trace并退出程序. package chapter; public class Main8 { /** * <p> *

Android应用开发中全局异常的捕获

最近在做个项目,需要在程序出现运行时异常和错误导致程序crash时进行一些操作,找到一个方法 Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {// 给主线程设置一个处理运行时异常的handler public void uncaughtException(Thread thread, final Throwable ex) { ex.printStackTrace(); //当程序出现crash时

WPF 线程中异常导致程序崩溃

原文:WPF 线程中异常导致程序崩溃 一般我们WPF中都加全局捕获,避免出现异常导致崩溃. Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException)

线程中的current thread not owner异常错误

多线程常用的一些方法: wait(),wait(long),notify(),notifyAll()等 这些方法是当前类的实例方法, wait()      是使持有对象锁的线程释放锁;wait(long)是使持有对象锁的线程释放锁时间为long(毫秒)后,再次获得锁,wait()和wait(0)等价;notify()    是唤醒一个正在等待该对象锁的线程,如果等待的线程不止一个,那么被唤醒的线程由jvm确定;notifyAll 是唤醒所有正在等待该对象锁的线程. 并且应该优先使用notify

Application中捕获APP中的全局异常

1 package com.example.administrator.mystudent; 2 3 import android.app.Application; 4 import android.util.Log; 5 6 import org.xutils.x; 7 8 /** 9 * Created by hyang on 2016/8/23. 10 */ 11 public class MyApplication extends Application { 12 private sta

C#中的异常捕获机制(try catch finally)

一.C#的异常处理所用到关键字 try :用于检查发生的异常,并帮助发送任何可能的异常. catch :以控制权更大的方式处理错误,可以有多个catch子句.  finally: 无论是否引发了异常,finally的代码块都将被执行.  throw: 用于引发异常,可引发预定义异常和自定义异常. 二.C#异常处理的格式 1 try 2 { 3 //程序代码块: 4 } 5 catch(Exception e) 6 { 7 //异常处理代码块: 8 } 9 finally 10 { 11 //无论