线程一1.0 线程 获取线程的运行时异常,Hook线程

下图:

线程在执行单元中是不允许抛出checked异常的,,而且线程运行在自己的上下文中,派生他的线程将无法直接获得它运行中出现的异常信息,对此Java给我们提供了一个uncaughtexceptionHandler接口,当线程在运行过程中出现异常时,就会回调UncaughtExceptionHandler接口,从而得知那个线程在运行中出错,以及出现了什么样的错误,如下

public static void main(String[] args) {
        try{
            Thread t =new Thread(new Runnable(){
                @Override
                public void run() {
                    int i = 10/0;
                    System.out.println("run....");
                }
            });
            t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t,   Throwable e) {
                    System.out.println("catch 到了"+t.getName());
                    e.printStackTrace();
                }
            });
            t.start();
        }catch(Exception e){
            System.out.println("catch 不到");
        }
    }

结果如下:

public static void main(String[] args) {
        try{
            Thread t =new Thread(new Runnable(){
                @Override
                public void run() {
                    int i = 10/0;
                    System.out.println("run....");
                }
            });
            t.start();
        }catch(Exception e){
            System.out.println("catch 不到");
        }
    }

二》 uncaughtExceptionHandker 源码分析

在没有向线程注入uncaughtExceptionHandler回调接口的时候线程若是出现了异常该怎么办?

get方法首先会判断当前线程中是否设置了handler,有则执行线程自己的uncaughtException 方法,否则就到 所在的threadgroup中获取,threadgroup同样也实现了uncaugthExceptionHandler接口,看看threadgroup里面的uncaught-Exception:

该threadgroup如果有父threadgroup,则直接调用父group的uncaughtException,

如果设置了全局默认的uncaughtexceptionHandler,则调用uncaughtException方法,

若既没有父threadgroup,也没有设置全局的默认uncaughtException,则会直接将异常的堆栈信息定向到system,err中

public static void main(String[] args) {
        ThreadGroup maingroup=Thread.currentThread().getThreadGroup();
        System.out.println(maingroup.getName());
        System.out.println(maingroup.getParent());
        System.out.println(maingroup.getParent().getParent());
        final Thread thread=new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                // TODO: handle exception
            }
        
            System.out.println(1/0);
        },"test-thread");
        thread.start();
    }

上面代码没有设置默认的handler,也没有对thread指定handler,因此当thread出现异常的时候,会向上寻找group的uncaughtException,如图:

三  注入钩子线程:

hook线程:

jvm的退出是由于jvm进程中没有活跃的非守护线程,或者收到了系统的中断信号,向jvm程序中注入了一个Hook线程,在jvm进城退出的时候,Hook线程会启动执行,通过Runtime可以为jvm注入多个Hook线程,例子:

public static void main(String[] args) {
     Runtime.getRuntime().addShutdownHook(new Thread() {
         @Override
        public void run() {
             try {
                 System.out.println("the hook thread 1 is running.");
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
                
            }
             System.out.println("the hook thread 1 will exit.");
        }
     });
     //可以注入多个
     Runtime.getRuntime().addShutdownHook(new Thread() {
         @Override
        public void run() {
             try {
                 System.out.println("the hook thread 2 is running.");
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
                
            }
             System.out.println("the hook thread 2 will exit.");
        }
     });
    } 

java 注入了两个Hook线程,在main线程中结束,也就是jvm中没有了活动的非守护线程,jvm即将推出的时候,两个Hook线程会被启动并且运行,结果如下:

在我们开发中经常会遇到Hook线程,比如为了防止某个程序被重复启动,在进程启动时会创建一个lock文件,进程收到中断信号的时候会删除这个lock文件的存在, 模拟一个利用Hook线程防止重复启动的程序,;

public class ThreadGroupCreator {
    private final static String LOCK_PATH="/wenjian/home";
    private final static String LOCK_FILE=".lock";
    private final static String PERMISSIONS="rw-------";
    public static void main(String[] args) throws IOException{
    Runtime.getRuntime().addShutdownHook(new Thread(()->{
        System.out.println("the program received kill signal.");
        getLockFile().toFile().delete();
        
    }));
        checkckRunning();
        for(; ;) {
            try {
                TimeUnit.MILLISECONDS.sleep(1);
                System.out.println("program is running.");
            } catch (Exception e) {
                e.printStackTrace();
                
            }
        }
    }
    private static void checkckRunning() throws IOException {
        Path path=getLockFile();
        if (path.toFile().exists()) {
            throw new RuntimeException("the program already running.");
        }
        Set<PosixFilePermission>perms=PosixFilePermissions.fromString(PERMISSIONS);
        Files.createFile(path, PosixFilePermissions.asFileAttribute(perms));
    }
    private static Path getLockFile() {
        return java.nio.file.Paths.get(LOCK_PATH, LOCK_FILE);
    }
}

运行发现多了个.lock文件

执行kill pid jvm收到中断信号,并且启动Hook线程,删除。,lock 文件 输出如下

 

Hook线程只会在收到退出信号的时候会被执行,如果在kill的时候使用了参数-9,那么Hook线程不会得到执行,进程会立即退出,因此。lock文件得不到清理

Hook线程中也可以执行一些资源释放的工作,比如关闭文件句柄,socket 连接。数据库的connection等

尽量不要再Hook线程中执行一些非常耗时非常浪费时间的操作,会导致程序迟迟不能退出

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值