zoukankan      html  css  js  c++  java
  • 【Thread】多线程的异常处理?

    线程中处理异常是个头疼的问题,在异步的代码中,如何将异常捕获

    捕获异常后,将异常反馈给开发者,或最终用户

    一、将异常反馈给开发者,一般来说,在日志中打印异常日志即可;

    二、将异常反馈给最终用户,一般来说,在页面上弹出提示框即可。

    将异常打印到日志中(UncaughtExceptionHandler方式处理异常)

    在线程当中遇到异常,需要捕捉并打印日志,我们可以考虑使用UncaughtExceptionHandler的回调

    以下是一个简单的测试例子。

    有一个线程,方法体中会抛出异常

    package com.nicchagil.study.thread.thinking.No15线程异常的捕捉;
    
    public class ExceptionThread implements Runnable {
    
        @Override
        public void run() {
            throw new NullPointerException("故意抛出的异常。");
        }
    
    }
    View Code

    自定义的异常处理器,这里所做的操作仅是在控制台打印异常,告诉开发者(并非用户哦)有这么个异常发生

    package com.nicchagil.study.thread.thinking.No15线程异常的捕捉;
    
    import java.lang.Thread.UncaughtExceptionHandler;
    
    public class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
    
        @Override
        public void uncaughtException(Thread t, Throwable throwable) {
            System.out.println("Print exception @ MyUncaughtExceptionHandler -> " + throwable);
        }
    
    }
    View Code

    为设置方便,通常可以有一个线程工厂(当然也可以直接设置,不通过工厂了),已帮忙批量以通用的参数设置产生线程。见调用类可见,有触发方法使用到此工厂,也有直接实例化线程的。

    package com.nicchagil.study.thread.thinking.No15线程异常的捕捉;
    
    import java.util.concurrent.ThreadFactory;
    
    public class HandlerThreadFactory implements ThreadFactory {
    
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
            return t;
        }
    
    }
    View Code

    触发类,这里介绍两种设置方式(一直接设置;一通过工厂设置),还有一种错误的设置方式,以供借镜。

    package com.nicchagil.study.thread.thinking.No15线程异常的捕捉;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Call {
    
        public static void main(String[] args) {
            /* Work */
            callByWay1();
            
            /* Work */
            callByWay2();
            
            /* Un-work */
            callByWay3();
        }
        
        /**
         * 原始的调用方式,在Thread对象设置异常处理器
         */
        public static void callByWay1() {
            try {
                Thread t = new Thread(new ExceptionThread());
                t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
                t.start();
            } catch (Throwable t) {
                System.out.println("Print exception @ Call.java -> " + t);
            }
        }
        
        /**
         * 使用工厂设置异常处理器的调用方式
         */
        public static void callByWay2() {
            try {
                ExecutorService es = Executors.newCachedThreadPool(new HandlerThreadFactory());
                es.execute(new ExceptionThread());
            } catch (Throwable t) {
                System.out.println("Print exception @ Call.java -> " + t);
            }
        }
        
        /**
         * 实例化Thread后,传入ExecutorService调用。(此方法不行,原因见代码内注释)
         */
        public static void callByWay3() {
            try {
                Thread t = new Thread(new ExceptionThread());
                t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
                
                ExecutorService es = Executors.newCachedThreadPool();
                /* 此方法不行,异常处理器虽在前面设置,但由于es.execute(t)的入参为Runnable,故异常处理器遗失 */
                es.execute(t);
            } catch (Throwable t) {
                System.out.println("Print exception @ Call.java -> " + t);
            }
        }
    
    }
    View Code

    日志如下,可见:

    1. 如果正确设置了异常处理器,将会触发异常处理器。我们的异常处理器中书写的逻辑是打印日志,所以,日志打印出来了。
    2. 无论有无设置异常处理器,异常都不会被直接的catch块捕捉和打印
    Print exception @ MyUncaughtExceptionHandler -> java.lang.NullPointerException: 故意抛出的异常。
    Print exception @ MyUncaughtExceptionHandler -> java.lang.NullPointerException: 故意抛出的异常。
    Exception in thread "pool-1-thread-1" java.lang.NullPointerException: 故意抛出的异常。
        at com.nicchagil.study.thread.thinking.No15线程异常的捕捉.ExceptionThread.run(ExceptionThread.java:7)
        at java.lang.Thread.run(Thread.java:619)
        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)

    将异常提示给最终用户(捕获异常)

    告诉最终用户有异常发生,则需要捕获线程执行过程中产生的异常,捕获异常后传递给页面进行提示。

    执行线程,并捕获异常:

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class MyThread implements Callable<String> {
    
        @Override
        public String call() throws Exception {
            if (1 == 1) {
                throw new RuntimeException("模拟的一个异常");
            }
    
            return "hello world";
        }
    
        public static void main(String[] args) {
            ExecutorService es = Executors.newCachedThreadPool();
    
            try {
                Future<String> f = es.submit(new MyThread());
                System.out.println(f.get());
            } catch (Throwable t) {
                System.out.println("捕获异常 : " + t.getMessage());
            }
        }
    
    }
    View Code

    日志:

    看到“捕获异常 : java.lang.RuntimeException: 模拟的一个异常”就知道异常被捕捉了,剩下的就看你如何传到JSP了,哈哈哈。

    捕获异常 : java.lang.RuntimeException: 模拟的一个异常

    Runnable的线程也是可以的:

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class MyThread implements Runnable {
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " run.");
            throw new RuntimeException("模拟的一个异常");
        }
    
        public static void main(String[] args) {
            ExecutorService es = Executors.newCachedThreadPool();
            
            try {
                Future<?> f = es.submit(new MyThread());
                System.out.println(f.get());
            } catch (Throwable t) {
                System.out.println("捕获异常 : " + t.getMessage());
            }
        }
    
    }
    View Code
  • 相关阅读:
    Java Properties 类读配置文件保持顺序
    mysql在增加列前进行判断该列是否存在
    java中用jdom创建xml文档/将数据写入XML中
    JavaFX 简介
    ActiveMQ详细入门使用教程
    Jquery Pagination分页插件使用
    Jquery选择器总结
    自己4月份面试的一些总结
    Java面试题全集(下)转载
    Java面试题全集(上)转载
  • 原文地址:https://www.cnblogs.com/nick-huang/p/4768358.html
Copyright © 2011-2022 走看看