Java Thread线程异常监控

学习Java的同学注意了!!! 

学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:589809992 我们一起学Java!


一、场景描述:单线程程序可以用try...catch捕获程序的异常,而在多线程程序的时候是无法使用try...catch捕获。

示例1:多线程发生异常,无法使用try...catch捕获问题

public class NoCaughtThread  implements Runnable{
    @Override
    public void run() {
        System.out.println(3 / 2);
        System.out.println(3 / 0);
        System.out.println(3 / 1);
    }
    
    public static void main(String[] args) {
        try {
            Thread thread = new Thread(new NoCaughtThread());
            thread.start();
        } catch (Exception e) {
            System.out.println("==Exception: " + e.getMessage());
        }
    }
}

运行结果:

1

Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero

at threadtest.NoCaughtThread.run(NoCaughtThread.java:7)

at java.lang.Thread.run(Thread.java:724)

显然这并非程序设定异常捕获,此时try...catch无法捕获线程的异常。此时,如果线程因为异常而终止执行将无法检测到异常。究其原因Thread类run()方法是不抛出任何检查型异常的,而自身却可能因为一个异常而被中止。

二、解决方式大致有两种:① 在run()中设置对应的异常处理,主动方法来解决未检测异常;② Thread类API中提供Interface接口UncaughtExceptionHandler,该接口包含uncaughtException方法,它能检测出某个未捕获的异常而终结的情况;

示例2:主动的检测异常

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

public class InitiativeCaught {
    public static void main(String[] args) {
        InitialtiveThread initialtiveThread = new InitialtiveThread() ;
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(initialtiveThread);
        exec.shutdown();
    }
}

class InitialtiveThread implements Runnable {
    @Override
    public void run() {
        Throwable thrown = null;
        try {
            System.out.println(3 / 2);
            System.out.println(3 / 0);
            System.out.println(3 / 1);
        } catch (Throwable e) {
            thrown = e;
        } finally {
            threadDeal(this, thrown);
        }
    }
    
    public void threadDeal(Runnable r, Throwable t) {
        System.out.println("==Exception: " + t.getMessage());
    }  
}

运行结果:

1

==Exception: / by zero

此时是主动捕获异常并做处理,得到想要的结果。

示例3:Thread类API中提供UncaughtExceptionHandler接口捕获异常,要求检测线程异常,发生异常设置为重复调用三次之后结束线程。

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

public class ThreadMonitor implements Runnable {
    private int data;                    // 可设置通过构造传参
    private int control = 0;
    private static final int MAX = 3;    // 设置重试次数
    
    public ThreadMonitor(int i) {
        this.data = i;    
    }
    public ThreadMonitor() {
        // TODO Auto-generated constructor stub
    }
    
    @Override
    public void run() {
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread arg0, Throwable e) {
                // TODO Auto-generated method stub
                System.out.println("==Exception: " + e.getMessage());
                String message = e.getMessage();
                if( control==MAX ){
                    return ;
                }else if( "ok".equals(message) ){
                    return ;
                }else if ( "error".equals(message) ) {
                    new Thread() {
                        public void run() {
                            try {
                                System.out.println("开始睡眠。");
                                Thread.sleep(1 * 1000);
                                control++ ;
                                System.out.println("睡眠结束,control: "+ control);
                                myTask(data) ;
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        };
                    }.start();
                }else{
                    return ;
                }
            }
        });  
        
        myTask(data) ;
        
    }
    
    @SuppressWarnings("finally")
    public void myTask(int data){
        boolean flag = true ;
        try {
            System.out.println(4 / data);
        } catch (Exception e) {
            flag = false ;
        } finally {
            if( flag ){
                throw new RuntimeException("ok");
            }else{
                throw new RuntimeException("error");
            }
        }
    }
    
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        ThreadMonitor threadMonitor = new ThreadMonitor(0);
        exec.execute(threadMonitor);
        exec.shutdown();
    }
}

运行结果:

==Exception: error

开始睡眠。

睡眠结束,control: 1

==Exception: error

开始睡眠。

睡眠结束,control: 2

==Exception: error

开始睡眠。

睡眠结束,control: 3

==Exception: error

此时,可以正常捕获线程因除数为零造成的中断。其中:

(1) 在Thread类API中提供 Interface 接口UncaughtExceptionHandler,该接口包含一个uncaughtException方法,它能检测出某个由于未捕获的异常而终结的情况。定义如下:

UncaughtExceptionHandler接口: public static interface Thread.UncaughtExceptionHandler

uncaughtException方法: public void uncaughtException(Thread t, Throwable e)

(2) uncaughtException方法会捕获线程的异常,此时需要覆写该方法设定自定义的处理方式。

(3) 设置UncaughtExceptionHandler异常处理:

方式一:通过Thread提供的静态static方法,设置 默认 异常处理: public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler ux)

方式二:通过方法: public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

(4) UncaughtExceptionHandler异常处理需要设置在run()方法内,否则无法捕获到线程的异常。

学习Java的同学注意了!!! 

学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:589809992 我们一起学Java!

转载于:https://my.oschina.net/abcijkxyz/blog/851255

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值