zoukankan      html  css  js  c++  java
  • 使用UncaughtExceptionHandler重启线程

    先复习Java中的异常

    java.lang.Throwable  顶层父类

      |– Error错误:JVM内部的严重问题,如OOM,程序员无法在代码中无法处理。

      |–Exception异常:普通的问题。通过合理的处理,程序还可以回到正常执行流程。要求程序员要进行处理。

        |–RuntimeException:未检查异常(unchecked exception)。  这类异常是程序员的逻辑问题,由于程序员的疏忽导致的错误(如数组越界,空指针等)。

          Java编译器不进行强制要求处理。 也就是说,这类异常在程序中,可以进行处理,也可以不处理。 

        |–非RuntimeException:已检查异常(checked exception)、编译时异常。这类异常是由一些外部的偶然因素所引起的。Java编译器强制要求处理。也就是说,

          程序必须进行对这类异常进行处理,throw,throws或者try catch。

    而线程Thread的的run()方法不接受抛出语句,因此对于已检查异常,我们必须进行捕获并处理,如:

        @Override
        public void run() {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(new File(""));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
            }
        }

    而对于未检查异常,如果在run()方法中运行出现了未检查异常,那么默认的行为是将堆栈跟踪信息写到控制台中(或者记录到错误日志文件中),然后退出程序。

    Java为我们提供了一个机制,用来捕获并处理在一个线程对象中抛出的未检查异常,以避免程序终止。用UncaughtExceptionHandler来实现这种机制。

    不使用UncaughtExceptionHandler的场景:

    public class MyThread implements Runnable {
    
        @Override
        public void run() {
            Integer.parseInt("yangyongjie");
            System.out.println("expect");
        }
    
    }

    控制台输出:

    Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: "yangyongjie"
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Integer.parseInt(Integer.java:492)
        at java.lang.Integer.parseInt(Integer.java:527)
        at com.yang.spbo.other.thread.MyThread.run(MyThread.java:15)
        at java.lang.Thread.run(Thread.java:745)

    此时,线程立即终止,并没有执行之后的代码输出字符串“expect”。若在执行很重要的代码之前,出现了未检查异常,导致了线程终止,那么就会导致业务异常。

    使用UncaughtExceptionHandler后:

      首先,自定义异常处理器实现UncaughtExceptionHandler接口,用来捕获和处理运行时出现的未检查异常(RuntimeException)

    public class CustomUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(CustomUncaughtExceptionHandler.class);
    
        /**
         * 可以自定义处理方案,如不断重试、将异常任务存入数据库等
         *
         * @param t
         * @param e
         */
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            LOGGER.error("An exception has been captured,Thread: {}", t.getId());
            LOGGER.error("Exception: {}: {}", e.getClass().getName(), e.getMessage());
            LOGGER.error("Thread status: {}", t.getState());
            new Thread(new MyThread()).start();
        }
    }

    接着,将异常处理器添加到线程中

    public class MyThread implements Runnable {
    
        @Override
        public void run() {
            // 异常处理器
            Thread.currentThread().setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
            Integer.parseInt("yangyongjie");
            System.out.println("expect");
        }
    }

    再次运行,程序能够持续执行run方法。实际上,如果线程完成了任务,那么它在退出时不会抛出任何异常,从而完成自身生命周期

    An exception has been captured
    Thread: 10
    Exception: java.lang.NumberFormatException: For input string: "yangyongjie"
    Stack Trace: 
    java.lang.NumberFormatException: For input string: "yangyongjie"
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Integer.parseInt(Integer.java:492)
        at java.lang.Integer.parseInt(Integer.java:527)
        at com.yang.spbo.other.thread.MyThread.run(MyThread.java:22)
        at java.lang.Thread.run(Thread.java:745)
    Thread status: RUNNABLE
    An exception has been captured
    Thread: 12
    Exception: java.lang.NumberFormatException: For input string: "yangyongjie"
    Stack Trace: 
    java.lang.NumberFormatException: For input string: "yangyongjie"
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Integer.parseInt(Integer.java:492)
        at java.lang.Integer.parseInt(Integer.java:527)
        at com.yang.spbo.other.thread.MyThread.run(MyThread.java:22)
        at java.lang.Thread.run(Thread.java:745)
    Thread status: RUNNABLE

    请注意:UncaughtExceptionHandler可以在无需重启线程的条件下,将日志记录变得更加健壮,因为默认日志在线程执行失败时,不会提供足够的上下文信息。

    线程池异常处理,自定义创建线程的工厂,在创建线程时指定

    /**
     * 创建线程的工厂,指定有意义的线程组名称,方便回溯
     *
     * @author yangyongjie
     * @date 2019/8/14
     * @desc
     */
    public class CustomThreadFactory implements ThreadFactory {
        /**
         * 线程池中的线程名称前缀
         */
        private String namePrefix;
    
        /**
         * 用于线程的名称递增排序
         */
        private AtomicInteger atomicInteger = new AtomicInteger(1);
    
        public CustomThreadFactory(String whatFeaturOfGroup) {
            this.namePrefix = "From CustomThreadFactory-" + whatFeaturOfGroup + "-worker-";
        }
    
        @Override
        public Thread newThread(Runnable r) {
            String name = namePrefix + atomicInteger.getAndIncrement();
            Thread thread = new Thread(r, name);
            thread.setUncaughtExceptionHandler(new CustomUncaughtExceptionHandler());
            return thread;
        }
    }
    
    /**
     * 用来捕获和处理运行时出现的未检查异常(RuntimeException)
     * 并重启线程
     *
     * @author yangyongjie
     * @date 2019/11/26
     * @desc
     */
    public class CustomUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(CustomUncaughtExceptionHandler.class);
    
        /**
         * 可以自定义处理方案,如不断重试、将异常任务存入数据库等
         *
         * @param t
         * @param e
         */
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            LOGGER.error("uncaughtException:" + e.getMessage(), e);
            MDCUtil.removeWithOutContext();
        }
    }
  • 相关阅读:
    前端的一些小的效果
    数组排序
    查看字符串中出现次数最多的字符以及出现的次数
    让元素垂直水平居中的方式
    MySQL
    数组_集合转换
    Spring_使用(JDBC)
    Spring_Aop基于配置文件
    Spring_Aop_(二)
    Spring_总结
  • 原文地址:https://www.cnblogs.com/yangyongjie/p/11035392.html
Copyright © 2011-2022 走看看