用Thread中的UncaughtExceptionHandler来处理未捕获的异常

用Thread中的UncaughtExceptionHandler来处理未捕获的异常

Java中在处理异常的时候,通常的做法是使用try-catch-finally来包含代码块,但是Java自身还有一种方式可以处理

——使用UncaughtExceptionHandler。它能检测出某个线程由于未捕获的异常而终结的情况。当一个线程由于未

捕获异常而退出时,JVM会把这个事件报告给应用程序提供的UncaughtExceptionHandler异常处理器(这是

Thread类中的接口):

//Thread类中的接口
public interface UncaughtExceptionHanlder {
	void uncaughtException(Thread t, Throwable e);
}

在Java 5以后,可以通过以下实例方法来为每个线程设置一个UncaughtExceptionHandler:

Thread.setUncaughtExceptionHandler(UncaughtExceptionHandler handler);//实例方法

或者通过以下静态方法来设置默认的UncaughtExceptionHandler:

Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler);//静态方法

这些异常处理器中,只有一个将会被调用——JVM首先搜索每个线程的异常处理器,若没有,则搜索该线程的

ThreadGroup的异常处理器。ThreadGroup中的默认异常处理器实现是将处理工作逐层委托给上层的

ThreadGroup,直到某个ThreadGroup的异常处理器能够处理该异常,否则一直传递到顶层的ThreadGroup。顶

层ThreadGroup的异常处理器委托给默认的系统处理器(如果默认的处理器存在,默认情况下为空),否则把栈

信息输出到System.err。

下面是一个Example:

package com.example.one;

import java.lang.Thread.UncaughtExceptionHandler;

public class T {
    public static void main(String[] args) throws Exception {
        //为所有线程设置默认的未捕捉异常处理器
        Thread.setDefaultUncaughtExceptionHandler(new MyDefaultExceptionHandler());
        Thread.currentThread().setName("Main Thread");
        Thread thread = new Thread(new MyTask("MyTask"), "Child Thread");
        //为某个线程单独设置异常处理器
        thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        thread.start();
        //主线程抛出异常,将会使用默认的异常处理器
        throw new RuntimeException("IllegalArgumentException");
    }
}

class MyDefaultExceptionHandler implements UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("MyDefaultExceptionHandler: Thread: " + t.getName() + ", Message: " + e.getMessage());
    }
}

class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("MyUncaughtExceptionHandler: Thread: " + t.getName() + ", Message: " + e.getMessage());
    }
}

class MyTask implements Runnable {
    private String name;

    public MyTask(String name) {
        this.name = name;
    }

    public MyTask() {
    }

    public String getName() {
        return name;
    }

    @Override
    public void run() {
        throw new RuntimeException(name + " gets a NullPointerException");
    }
}
# 输出
MyUncaughtExceptionHandler: Thread: Child Thread, Message: MyTask gets a NullPointerException
MyDefaultExceptionHandler: Thread: Main Thread, Message: IllegalArgumentException

可以看到,Main Thread由于没有显式设置UncaughtExceptionHandler,其抛出的未捕获异常,被默认异常处理

器MyDefaultUncaughtExceptionHandler处理了,而Child Thread由于单独设置了

UncaughtExceptionHanlder,其抛出的未捕获异常,则被Thread本身自带的MyUncaughtExceptionHandler处

理了。

如果要为线程池中的所有线程设置一个UncaughtExceptionHandler,则需要为ThreadPoolExecutor的构造函数

提供一个自定义的ThreadFactory(与所有的线程操控一样,只有线程的所有者能够改变线程的

UncaughtExceptionHandler):

package com.example.two;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class T {
    public static void main(String[] args) throws Exception {
        //使用自定义的ThreadFactory来创建线程,并绑定同一个异常处理器
        UncaughtExceptionHandler handler = new MyUncaughtExceptionHandler();
        ExecutorService executor = Executors.newCachedThreadPool(new MyThreadFactory(handler));
        executor.execute(new MyTask("task1"));
        executor.execute(new MyTask("task2"));
        executor.execute(new MyTask("task3"));
        executor.shutdown();
    }
}

class MyTask implements Runnable {
    private String name;

    public MyTask(String name) {
        this.name = name;
    }

    public MyTask() {
    }

    public String getName() {
        return name;
    }

    @Override
    public void run() {
        throw new RuntimeException(name + " gets a NullPointerException");
    }
}

class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("MyUncaughtExceptionHandler: Thread: " + t.getName() + ", Message: " + e.getMessage());
    }
}

class MyThreadFactory implements ThreadFactory {
    private UncaughtExceptionHandler handler;

    public MyThreadFactory(UncaughtExceptionHandler handler) {
        this.handler = handler;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        //在这里设置异常处理器
        thread.setUncaughtExceptionHandler(handler);
        return thread;
    }
}
# 输出
MyUncaughtExceptionHandler: Thread: Thread-2, Message: task3 gets a NullPointerException
MyUncaughtExceptionHandler: Thread: Thread-1, Message: task2 gets a NullPointerException
MyUncaughtExceptionHandler: Thread: Thread-0, Message: task1 gets a NullPointerException

从结果中可以看出,线程池中的每个线程都使用同一个异常处理器来处理未捕获的异常。

不过,上面的结果能证明:通过execute提交的任务,能将它抛出的异常交给未捕获的异常处理器。

下面的例子只修改了main方法,以submit方式提交任务:

package com.example.three;

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

public class T {
    public static void main(String[] args) throws Exception {
        //使用自定义的ThreadFactory来创建线程,并绑定同一个异常处理器
        Thread.UncaughtExceptionHandler handler = new MyUncaughtExceptionHandler();
        ExecutorService executor = Executors.newCachedThreadPool(new MyThreadFactory(handler));
        //通过submit方法提交任务
        Future future1 = executor.submit(new MyTask("task1"));
        Future future2 = executor.submit(new MyTask("task2"));
        System.out.println(future1.get());
        System.out.println(future2.get());
        executor.shutdown();
    }
}

class MyTask implements Runnable {
    private String name;

    public MyTask(String name) {
        this.name = name;
    }

    public MyTask() {
    }

    public String getName() {
        return name;
    }

    @Override
    public void run() {
        throw new RuntimeException(name + " gets a NullPointerException");
    }
}

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("MyUncaughtExceptionHandler: Thread: " + t.getName() + ", Message: " + e.getMessage());
    }
}

class MyThreadFactory implements ThreadFactory {
    private Thread.UncaughtExceptionHandler handler;

    public MyThreadFactory(Thread.UncaughtExceptionHandler handler) {
        this.handler = handler;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        //在这里设置异常处理器
        thread.setUncaughtExceptionHandler(handler);
        return thread;
    }
}
# 输出
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException: task1 gets a NullPointerException
	at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
	at com.example.three.T.main(T.java:16)
Caused by: java.lang.RuntimeException: task1 gets a NullPointerException
	at com.example.three.MyTask.run(T.java:38)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)

结果告诉我们,future1.get的时候遇到了ExecutionException。那我们再来看看Future.get方法的实现(在

java.util.concurrent.FutureTask类中):

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)//如果任务没有结束,则等待结束
        s = awaitDone(false, 0L);
    return report(s);//如果执行结束,则报告执行结果
}

@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)//如果执行正常,则返回结果
        return (V)x;
    if (s >= CANCELLED)//如果任务被取消,调用get则报CancellationException
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);//执行异常,则抛出ExecutionException
}

源代码说明:如果一个由submit提交的任务由于抛出了异常而结束,那么这个异常将被Future.get封装在

ExecutionException中重新抛出。所以,通过submit提交到线程池的任务,无论是抛出的未检查异常还是已检查

异常,都将被认为是任务返回状态的一部分,因此不会交由异常处理器来处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值