多线程的异常处理机制

多线程的异常处理

在线程中如果出现异常想要额外进行一些操作的话,可以使用线程的异常处理机制,UncaughtExceptionHandler,这是线程的一个子接口,当一个未捕获的异常导致线程中断的时候JVM会使用thread.getUncaughtExceptionHandler()来查询线程的uncaughtExceptionHandler并将线程和异常作为参数传递给uncaughtException方法

java 复制代码
@FunctionalInterface
public interface UncaughtExceptionHandler {
  // 该方法是由jvm通过调用thread的dispatchUncaughtException方法来执行的
  void uncaughtException(Thread t, Throwable e);
}

如果当前线程没有显示的调用thread.setUncaughtExceptionHandler()方法设置处理器时,会默认使用该线程的线程组的uncaughtException()方法,线程组是实现了UncaughtExceptionHandler接口的,线程组是在线程实例化时就进行指定的

java 复制代码
public class ThreadGroup implements Thread.UncaughtExceptionHandler

自定义Handler

如何自定义handler并进行使用呢,那当然是要实现UncaughtExceptionHandler接口了,重写uncaughtException()方法即可

java 复制代码
class MyExceptionHandler implements Thread.UncaughtExceptionHandler{

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println(t.getName()+"抛出异常"+e.getMessage());
        e.printStackTrace();
    }
}

在实例化线程的时候进行指定handler即可

java 复制代码
thread.setUncaughtExceptionHandler(new MyExceptionHandler());

代码示例

java 复制代码
public class TestExceptionHandler {
    public static void main(String[] args) {
        TestExceptionHandler test = new TestExceptionHandler();
        Thread thread = new Thread(test.new MyThread());
        thread.setUncaughtExceptionHandler(new MyExceptionHandler());
        thread.start();

    }

    class MyThread implements Runnable{

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int i = 1/0;
        }
    }

}



class MyExceptionHandler implements Thread.UncaughtExceptionHandler{

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println(t.getName()+"抛出异常"+e.getMessage());
        e.printStackTrace();
    }
}

例子不是一个好例子,但是主要就是展示一下如何使用,场景自己把握

线程池中处理异常

在项目中很少自己去继承Thread来启动线程,更多的是使用线程池,而在线程池中UncaughtExceptionHandler是否生效呢?

在测试时发现,再调用executorService.execute方法的时候uncaughtException方法生效了,而在调用executorService.submit方法的时候,uncaughtException方法没有生效,这个是不是很奇怪

submit方法为什么获取不到异常

其实这是因为线程池中submit方法会将任务使用FutureTask进行包装,然后再将FutureTask对象传递给execute来进行执行

java 复制代码
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

而FutureTask在执行的时候,已经进行了try...catch处理

java 复制代码
try {
    result = c.call();
    ran = true;
} catch (Throwable ex) {
    result = null;
    ran = false;
    setException(ex);
}

// setException方法将异常信息存储在outcome变量中了,而且将state状态修改为了EXCEPTIONAL
protected void setException(Throwable t) {
  if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
    outcome = t;
    UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
    finishCompletion();
  }
}

在使用FutureTask的get操作来获取结果时

java 复制代码
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}
// 因为出现异常,此时s为EXCEPTIONAL,此时会将outcome中的值,也就是异常信息抛出来throw new ExecutionException((Throwable)x);
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);
}

处理异常

可以有两种方式来处理异常

  • 方式一:在执行任务的Runnable的run方法中添加try...catch来手动捕获异常

  • 方式二:继承ThreadPoolExecutor来重写afterExecute方法,处理异常

    java 复制代码
    class MyThreadPoolExecutor extends ThreadPoolExecutor {
    
        public MyThreadPoolExecutor(int corePoolSize) {
            this(corePoolSize,corePoolSize,0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>());
        }
        public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }
    
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r,t);
            if (t == null && r instanceof Future<?>) {
                try {
                    Object result = ((Future<?>) r).get();
                } catch (CancellationException ce) {
                    t = ce;
                } catch (ExecutionException ee) { // 根据上述分析可知,FutureTask抛出来的异常是ExecutionException,所以捕获该异常,就可以获取到任务中所抛出来的异常
                    t = ee.getCause();
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt(); // ignore/reset
                }
            }
          // 此时如果t不为null,说明在进行get操作时有异常出现
            if(t != null){
                System.out.println("afterExecute获取异常"+t.getMessage());
            }
        }
    }

这个afterExecute是在线程池执行任务的过程中调用的

java 复制代码
Throwable thrown = null;
try {
  // 执行任务
    task.run();
} catch (RuntimeException x) {
    thrown = x; throw x;
} catch (Error x) {
    thrown = x; throw x;
} catch (Throwable x) {
    thrown = x; throw new Error(x);
} finally {
    afterExecute(task, thrown);
}

zhhll.icu/2020/多线程/基础...

本文由mdnice多平台发布

相关推荐
SimonKing4 分钟前
震惊:怎么回事,opencode一轮对话耗尽免费额度
java·后端·程序员
装不满的克莱因瓶8 分钟前
【SpringAI Alibaba】ReactAgent响应SSE实时流式传输案例实战
java·人工智能·ai·sse·springai
杨凯凡11 分钟前
Spring Boot 文件上传怎么做才安全?流式落盘、路径校验与数据库一致性实战
java·spring boot·文件上传·后端安全·数据库一致性
这个昵称不容易37 分钟前
旅游管理系统的设计与实现(SpringBoot + MySQL + Vue 前后端分离)
java·springboot·毕设
Rain的Java大神之路38 分钟前
LeetCode 37 解数独面试攻略:回溯 + 位掩码 + MRV,从暴力到毫秒级求解
java·后端·leetcode
天天被压力2 小时前
【A股分钟级高频数据完全手册 #11】券商实时行情数据接口:实时交易(券商源)接入与字段
java·人工智能·python
天天被压力2 小时前
【A股分钟级高频数据完全手册 #12】股票五档盘口数据接口:买卖五档实时盘口解析
java·人工智能·python
Wang's Blog11 小时前
Java 项目实战: 交友项目 - FastDFS:入门案例
java·开发语言·交友
apcipot_rain12 小时前
C++转Java日记(03)
java·开发语言·c++
皮皮林55112 小时前
Java实现微信扫码登录
java