父子线程之间的异常捕获

前言

什么是父子线程

我们首先看下 Java Thread 中的初始化过程。

以下方法来自于
Thread (ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals)
在这里插入图片描述
在这个方法中,可以看到在创建新线程的时候,继承了一些当前线程的参数作为初始化值。比如我们熟知的 inheritableThreadLocals

所以说,对Java中的线程,父线程的概念,只是一种逻辑称呼,创建线程的当前线程就是新线程的父线程,新线程的一些资源来自于这个父线程。

父线程的准确称呼应该被叫做当前线程的创建线程。

当听到父线程的说法时,应该立即联想到的是创建线程,创建新线程时一些资源的供给者。

一个线程与被他创建出来的线程,除了在创建的时候(init)会有一定的依赖交互之外,对JVM来说,他们并没有什么特别的依赖联系,两个独立的线程。

正文

Case 1

Thread.start 方式使用线程

在这里插入图片描述
在这里插入图片描述
可以看到,在上面的例子中,主线程并没有 catch 到子线程的异常。

case 2

ThreadPool.execute

在这里插入图片描述
在这里插入图片描述
使用 ThreadPool.execute () 方法,也没办法在父线程中捕获异常进行处理。

ThreadPool.submit

在这里插入图片描述
在这里插入图片描述
可以看到,使用 future.get 可以在主线程中 catch 到子线程的异常进行处理。

源码分析:

futureTask.get -> futureTask.report 根据 futureTask的状态返回结果.

几种状态如下:

	private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;
private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

futureTask 实现了 RunnableFuture ,在任务执行的时候执行 run 方法;在 set 中修改 futuretask 的状态。

public void run() {
        if (state != NEW ||
            !RUNNER.compareAndSet(this, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    
	protected void set(V v) {
        if (STATE.compareAndSet(this, NEW, COMPLETING)) {
            outcome = v;
            STATE.setRelease(this, NORMAL); // final state
            finishCompletion();
        }
    }

处理方案

当然,我们可以在子线程中处理异常,这是没问题的。
也可以通过向 Thread 对象传递异常处理器,可以实现在发生异常时候,按照对应的逻辑处理异常。
即,实现 Thread.UncaughtExceptionHandler 这个方法来进行处理。

Thread.UncaughtExceptionHandler

public class Driver {

    public static void main(String[] args) {

        ExecutorService threadPool = Executors.newFixedThreadPool(1);

        Thread.setDefaultUncaughtExceptionHandler(new ChildExceptionHandler());

        try {
            threadPool.execute(() -> {
                Car.run();
            });

        }catch (RuntimeException e) {
            System.out.println("main thread catch sub-thread-1 exception");
        }

    }

    static class Car{

        static void run(){
            throw new RuntimeException("no oil ");
        }

    }

    static class ChildExceptionHandler implements Thread.UncaughtExceptionHandler {

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println(String.format("thread [%s] happen exception [%s]", t, e.getMessage()));
        }
    }
}

在这里插入图片描述

ThreadPool.execute 的处理

通过阅读 ThreadPool.execute 的源码(本文就不详细介绍了),我们可以看到最后是通过 Woker 线程执行的 task 任务,runWorker 源码如下:
在这里插入图片描述
可以看到有 afterExecute 方法可以在执行后进行调用,或者发生异常的时候进行调用。我们可以实现这个方法来进行处理。

说明:上述代码 基于JDK15.

总结

  • 介绍了父子线程关系。
  • 三种不同的 case 。
  • 处理方案,可以通过 Thread.UncaughtExceptionHandler
  • 实际开发中使用线程池,可以通过 future.get 处理异常(推荐)

几个问题

  1. linux 线程模型与Java线程模型。
  2. Thread 与 ThreaLocal 的关系,及 ThreadLocal 为什么会内存泄漏。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值