捕获线程异常与Hook线程

捕获线程运行时异常API
void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)
为某个线程指定uncaughtExceptionHandler
static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
设置全局的uncaughtExceptionHandler
static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
获取全局的uncaughtExceptionHandler
UncaughtExceptionHandler getUncaughtExceptionHandler()
获取特定线程的uncaughtExceptionHandler

UncaughtExceptionHandler介绍
线程在执行单元中是不允许抛出checked异常的,在线程的运行上下文中,派生它的线程无法直接获取它运行中得异常信息。对此java提供了UncaughtExceptionHandler接口,当线程运行过程中出现异常时会回调UncaughtExceptionHandler接口,从而得知是哪个线程运行时出了什么样的错。

	@FunctionalInterface
    public interface UncaughtExceptionHandler {
  
        void uncaughtException(Thread t, Throwable e);
    }
    
	 private void dispatchUncaughtException(Throwable e) {
        getUncaughtExceptionHandler().uncaughtException(this, e);
    }

UncaughtExceptionHandler 是一个FunctionalInterface,只有一个抽象方法,当线程运行过程中出现异常时,JVM会调用dispatchUncaughtException将对应线程的异常信息传递给回调接口。

getUncaughtExceptionHandler源码

    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }

方法首先会判断当前线程是否设置了uncaughtExceptionHandler ,有则执行线程自己的uncaughtException(),否则就到ThreadGroup中获取。
ThreadGroup 的uncaughtException源码

 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);
            }
        }
    }

该线程如果有父ThreadGroup则直接调用父Group的uncaughtException(),如果设置了全局默认的UncaughtExceptionHandler,则调用uncaughtException(),既没有设置全局UncaughtExceptionHandler也没有父ThreadGroup则直接将异常信息定向到System.err中。

UncaughtExceptionHandler 测试代码

	public static void main(String[] args) {
		Thread.setDefaultUncaughtExceptionHandler((t,e)->{
			System.out.println(t.currentThread().getName()+"抛出异常  " +e.getMessage());
			e.printStackTrace();
		});
		new Thread(()->{
			System.out.println(1/0);
		},"异常线程1").start();
	}

输出
异常线程1抛出异常 / by zero
java.lang.ArithmeticException: / by zero
at ThreadException.ExceptionTest.lambda$1(ExceptionTest.java:11)
at java.lang.Thread.run(Unknown Source)

Hook线程介绍(钩子线程)
JVM进程的退出是由于没有活跃的非守护线程,或者收到了系统中断信号,向JVM程序注入一个Hook线程,在JVM进程退出的时候,Hook线程才会启动执行。可以通过Runtime向JVM注入多个Hook线程。

hook线程注入例子

	public static void main(String[] args) {
		Runtime.getRuntime()
				.addShutdownHook(new Thread(()->{
					System.out.println("hook test 1 running");
					try {
						TimeUnit.SECONDS.sleep(1);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("hook test 1 exit");
				},"HOOK线程1"));
		
		Runtime.getRuntime()
		.addShutdownHook(new Thread(()->{
			System.out.println("hook test 2 running");
			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("hook test 2 exit");
		},"HOOK线程2"));
		
		System.out.println("main thread  exit");
	}

输出
main thread exit
hook test 2 running
hook test 1 running
hook test 1 exit
hook test 2 exit

Hook线程注意事项及使用场景:

  1. Hook线程只有在收到JVM进程退出信号的时候才会被执行。
  2. Hook线程可以执行一些资源释放工作,比如关闭文件句柄,socket链接等。
  3. 尽量不要在Hook线程中执行耗时长的操作,会导致程序迟迟不能退出。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值