Java子线程中未捕获的RuntimeException会静默终止线程且不传播异常,主线程无感知;需通过Thread.setDefaultUncaughtExceptionHandler或setUncaughtExceptionHandler捕获,ExecutorService中Runnable异常无法通过Future.get()获取,应改用Callable。

当Runnable的run方法抛出未被捕获的非受检异常(如RuntimeException及其子类)时,该线程会立即终止执行,且不会影响其他线程的运行。
线程会静默死亡,不传播异常
Java线程模型规定:线程执行体(run方法)中未捕获的异常,不会向上抛给启动它的代码,也不会中断其他线程。JVM会直接终结当前线程,并打印异常堆栈到标准错误流(通常是控制台),但这个过程对调用方完全透明。
- 主线程启动一个新线程执行Runnable,若run中抛出NullPointerException,主线程照常运行,不会收到任何通知
- ThreadPoolExecutor中提交的任务若抛出非受检异常,线程池会捕获该异常、终止该工作线程,并可能创建新线程继续处理后续任务
- 异常不会触发Thread.UncaughtExceptionHandler,除非你显式设置过——默认情况下它只是把异常打印到System.err
默认的未捕获异常处理器只做日志输出
每个Thread都有一个UncaughtExceptionHandler,默认是ThreadGroup的实现,其行为就是调用System.err.println输出异常信息。这意味着:没有日志框架、没有告警、没有重试,只有控制台里一闪而过的堆栈。
- 可通过
Thread.setDefaultUncaughtExceptionHandler(...)设置全局处理器 - 也可对单个线程调用
thread.setUncaughtExceptionHandler(...) - 常见做法是在处理器中记录日志、上报监控、或触发熔断逻辑
线程池中的异常需特别关注
使用ExecutorService提交Runnable时,run方法异常不会反映在Future结果中(因为Runnable无返回值),也无法通过get()捕获。这容易造成“任务失败但无人知晓”的情况。
立即学习“Java免费学习笔记(深入)”;
- 建议优先使用Callable代替Runnable,以便利用Future.get()捕获并处理异常
- 若必须用Runnable,可在run方法内用try-catch兜底,主动记录异常并通知监控系统
- 自定义ThreadPoolExecutor可重写
afterExecute(Runnable r, Throwable t)来统一处理任务异常
不是所有异常都会导致线程死亡
只有未被try-catch捕获的异常才会终止线程。如果run方法内部已妥善处理了RuntimeException,线程会继续执行后续代码,甚至正常结束。
- 空指针、数组越界、类型转换失败等,只要没被catch,就直接终结线程
- 受检异常(Exception及其子类,不含RuntimeException)不能直接抛出,否则编译失败;必须捕获或声明throws,而Runnable接口不允许throws声明,所以实际无法从run中抛出受检异常
- ThreadDeath是唯一被JVM特殊对待的非受检异常,它由stop()方法抛出,已被弃用


















