线程异常处理与勾子程序

一:线程运行时异常

1、
由于线程不能抛出可以捕获异常,所以需要线程出现异常时jvm调用dispatchUncaughtException方法回调线程异常处理器处理(如果设置了该线程异常处理就是用设置的,没有就是用父线程组的,不然就使用全局线程处理,再不然就直接打印错误)。

jvm调用dispatchUncaughtException方法链路源码:

private void dispatchUncaughtException(Throwable e) {
	//获取相应的异常处理器
    getUncaughtExceptionHandler().uncaughtException(this, e);
}

//有设置异常处理器就使用设置的异常处理器,没有就用线程组的
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
//该线程通过setUncaughtExceptionHandler方法设置uncaughtExceptionHandler 
    return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group;
}

//如下就是线程组的异常处理,先使用父线程组,然后默认全局异常处理,作用打印
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);
        }
    }
}

2、Thread提供的线程处理异常的方法,如下:

//设置某个线程的unchecked异常回调方法
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)
//设置全局的线程unchecked异常回调方法
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
//获取某个线程的异常回调处理方法
public UncaughtExceptionHandler getUncaughtExceptionHandler()
//获取全局异常回调处理方法
public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()

案例使用:

/**
 * 捕获线程运行时异常
 */
public class CaptureThreadException {
    public static void main(String[] args){
        //设置回调接口
        Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
            System.out.println(t.getName()+" occur exception");
            e.printStackTrace();
        });

        final Thread thread=new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {

            }
            //这里会出现unchecked异常
            System.out.println(1/0);
        },"Test-Thread");
        thread.start();
    }
}
输出:
Test-Thread occur exception
java.lang.ArithmeticException: / by zero
	at chapter07.CaptureThreadException.lambda$main$1(CaptureThreadException.java:24)
	at java.lang.Thread.run(Thread.java:748)

如果注释回调接口,也会抛出运行时异常,因为也父线程组没有设置异常处理,也没有全局默认异常处理。
java.lang.ArithmeticException: / by zero
at chapter07.CaptureThreadException.lambda$main$1(CaptureThreadException.java:24)
at java.lang.Thread.run(Thread.java:748)

一、hook(勾子)程序

jvm没有活跃的非守护线程或者系统中断信号,jvm退出时将会执行多个勾子程序。

应用:zookeeper、kafka、mysql服务器都有一个.lock文件,用来防止程序重复启动,进程收到中断信号时删除这个文件

public class ExitCaptor {

    public static void main(String[] args) {
        int i = 0;
        //勾子程序,在出现异常或者被kil时调用(不能是kill -9)
        Runtime.getRuntime().addShutdownHook(new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("The application will be shutdown, do notifyAndRelease");
                        notifyAndRelease(); //资源关闭、消息提醒、日志
                    }
                }
        ));

        while (true) {
            try {
                Thread.sleep(1000);
                System.out.println("I am  working ........");
            } catch (InterruptedException e) {
            }
            if (++i >20){
                throw new RuntimeException("is error .....");
            }
        }

    }

    private static void notifyAndRelease() {
        System.out.println("doing notifyAndRelease.....");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("Resource、connnction and file is close.....");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("do notifyAndRelease gone.....");
        }

    }
}

控制台:

I am  working ........
I am  working ........
I am  working ........
I am  working ........
I am  working ........
I am  working ........
I am  working ........
I am  working ........
Exception in thread "main" java.lang.RuntimeException: is error .....
	at com.zixue.springboot_web02.p2c.ExitCaptor.main(ExitCaptor.java:26)
二、程序kill,做一些保护措施
public class ExitCaptor {

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("The application will be shutdown, do notifyAndRelease");
                        notifyAndRelease(); 
                    }
                }
        ));

        while (true) {
            try {
                Thread.sleep(1000);
                System.out.println("I am  working ........");
            } catch (InterruptedException e) {
            }
        }

    }

    private static void notifyAndRelease() {
        System.out.println("doing notifyAndRelease.....");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("Resource、connnction and file is close.....");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("do notifyAndRelease gone.....");
        }

    }
}

linux显示如下 kill 5664

I am  working ........
I am  working ........
I am  working ........
I am  working ........
I am  working ........
The application will be shutdown, do notifyAndRelease
doing notifyAndRelease.....
I am  working ........
I am  working ........

**linux显示如下 kill - 9 5664

I am  working ........
I am  working ........
I am  working ........
I am  working ........
I am  working ........
已杀死

总结:不建议使用kill -9 杀死application,会无法执行一些勾子程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值