自定义线程池的异常捕获

402 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情

多线程下的异常处理

JVM的这种设计源自于这样一种理念:“线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。”在Java程序中,无论线程发生checked exception还是unchecked exception异常,都应该在线程代码边界之内(run方法内)进行try catch并处理掉。换句话说,我们不能捕获从线程中逃逸的异常。

在线程内部没有进行异常捕获的话,异常会一直往上抛出,一旦被run方法抛出后,就不能在程序代码中使用try-catch中对异常进行捕获, 最终只能被jvm捕获出来。这时候我们不能对当发生异常时对程序作一些其他的处理。

public class ThreadException {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
​
}
class  MyThread extends Thread{
​
    @Override
    public void run() {
        System.out.println("线程执行的名称"+Thread.currentThread().getName());
        // 模拟发生异常
        int a = 1/0;
    }
}

1666944029051.png 接下来我们来修改主线程运行的代码来对子线程MyThread的异常进行捕获。

public class ThreadException {
    public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.start();
        } catch (Exception e) {
            System.out.println("捕获到了异常");  // 没有输出
            e.printStackTrace();
        }
    }
​
}
class  MyThread extends Thread{
​
    @Override
    public void run() {
        System.out.println("线程执行的名称"+Thread.currentThread().getName());
        // 模拟发生异常
        int a = 1/0;
    }
}

1666944029051.png

结果依然和没有进行try-catch的输出一致,这是由于在run方法的异常没有进行捕获,往上抛出后不能再在程序里面进行异常捕获。只能由于jvm来捕获异常,在Thread的类里面有一个dispatchUncaughtException方法,它调用了getUncaughtExceptionHandler获取用来处理未捕获异常的handler,然后向改handler分派未捕获的异常。

​
     // 返回当此线程因未捕获异常而突然终止时调用的处理程序。如果该线程没有显式设置未捕获异常处理程序,则返回该线程的ThreadGroup对象,除非该线程已终止,在这种情况下返回null。
    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }
​
​
     // 设置在此线程因未捕获异常而突然终止时调用的处理程序。 通过显式设置其未捕获异常处理程序,线程可以完全控制如何响应未捕获异常。如果没有设置这样的处理程序,那么线程的ThreadGroup对象将充当它的处理程序
    public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        checkAccess();
        uncaughtExceptionHandler = eh;
    }
​
     // 将未捕获的异常分派给处理程序。该方法仅由JVM调用
    private void dispatchUncaughtException(Throwable e) {
        getUncaughtExceptionHandler().uncaughtException(this, e);
    }

当未设置uncaughtExceptionHandler的时候getUncaughtExceptionHandler的方法默认返回一个group,使用线程默认的handler,即该线程所属的线程组。ThreadGroup的一个handler,因为他实现Thread.UncaughtExceptionHandler接口,并实现了默认的处理方法,这个方法里主要调用了System.err 进行输出,也就是直接打印到控制台。

public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread ""
                                 + t.getName() + "" ");
                e.printStackTrace(System.err);
            }
        }
    }

自定义处理线程异常

public class ThreadException {
    public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.setUncaughtExceptionHandler();
            thread.start();
        } catch (Exception e) {
            System.out.println("捕获到了异常"); // 不执行
            e.printStackTrace();
        }
    }
​
}
class  MyThread extends Thread{
​
    @Override
    public void run() {
        System.out.println("线程执行的名称"+Thread.currentThread().getName());
        // 模拟发生异常
        int a = 1/0;
    }
    public void setUncaughtExceptionHandler() {
        this.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("捕获线程里面run方法抛出的异常:"+e.getMessage());
            }
        });
    }
}

1666945564841.png

线程池中的线程异常

自定义捕获线程池中的异常,需要知道线程池中的线程是如何产生的,然后对每一个Thread设置自定义的异常处理。有一个默认的线程工厂负责生产一个个的线程,Executors.defaultThreadFactory()

    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
​
        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }
​
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

源代码如上所示,由于DefaultThreadFactory是Executors类的内部类,我们不能直接继承该类,只能实现该工厂类的接口——ThreadFactory接口,来实现我们自己的线程工厂。

class MyDefaultThreadFactory implements ThreadFactory{
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
​
    MyDefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                poolNumber.getAndIncrement() +
                "-thread-";
    }
​
    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                namePrefix + threadNumber.getAndIncrement(),
                0);
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        if (t.isDaemon()) {
            t.setDaemon(false);
        }
        if (t.getPriority() != Thread.NORM_PRIORITY) {
            t.setPriority(Thread.NORM_PRIORITY);
        }
        return t;
    }
}
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("捕获线程抛出的异常:" + e.getMessage());
    }
}