主线程能通过 try-catch 捕获子线程抛出的异常吗
主线程无法直接通过 try-catch 捕获子线程抛出的异常,因为子线程运行在独立的执行栈中,其异常不会传播到创建它的线程(主线程),try-catch 只能捕获当前线程(即主线程)抛出的异常。
主线程怎么捕获子线程异常
使用 Callable + Future(线程池,如果使用线程池提交 Callable 任务,可以通过 Future.get() 捕获执行过程中抛出的异常。
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(() -> {
throw new RuntimeException("子线程异常");
});
try {
future.get(); // 会抛出 ExecutionException,包装了原始异常
} catch (ExecutionException e) {
Throwable cause = e.getCause(); // 获取原始异常
System.out.println("捕获子线程异常: " + cause);
} catch (InterruptedException e) {
// 处理中断
} finally {
executor.shutdown();
}
为每个线程单独设置异常处理器
为每个线程单独设置异常处理器,当该线程抛出未捕获异常时,会自动回调处理器。
Thread t = new Thread(() -> {
throw new RuntimeException("子线程异常");
});
t.setUncaughtExceptionHandler((thread, throwable) -> {
System.out.println("线程 " + thread.getName() + " 发生异常: " + throwable.getMessage());
});
t.start();
设置全局默认异常处理器
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
System.out.println("默认处理器捕获: " + throwable);
});
new Thread(() -> { throw new RuntimeException("test"); }).start();
通过 ThreadGroup 处理
线程组可以重写 uncaughtException 方法来统一处理组内线程的异常。
ThreadGroup group = new ThreadGroup("MyGroup") {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程组捕获: " + e);
}
};
new Thread(group, () -> { throw new RuntimeException("异常"); }).start();
使用 CompletableFuture 的异常处理
CompletableFuture 是 Java 8 引入的一个增强版异步任务工具类,位于 java.util.concurrent 包。它同时实现了 Future 和 CompletionStage 接口,核心作用是以声明式、非阻塞的方式编排异步任务链。
CompletableFuture 提供了丰富的异步编程模型,可以方便地捕获异常。
CompletableFuture.runAsync(() -> {
throw new RuntimeException("异常");
}).exceptionally(ex -> {
System.out.println("异步任务异常: " + ex.getCause());
return null;
});
它解决了 Future 的什么痛点? 传统 Future 主要有两个缺陷:
-
阻塞获取结果:调用 future.get() 会一直阻塞线程直到任务完成。
-
无法主动回调:任务完成后不能自动执行后续逻辑,需要手动轮询或阻塞等待。
CompletableFuture 通过回调函数 + 流式链式调用解决了这些问题,让你可以像写同步代码一样组织异步流程。
核心能力
|
功能 |
常用方法示例 |
说明 |
|
提交异步任务 |
supplyAsync(() -> "结果") |
有返回值的异步任务 |
|
无返回值任务 |
runAsync(() -> System.out.println(…)) |
纯异步动作 |
|
转换结果 |
.thenApply(s -> s + "处理") |
拿到上一步结果,处理后返回新结果 |
|
消费结果 |
.thenAccept(System.out::println) |
拿到结果后执行操作,不返回 |
|
组合两个独立任务 |
.thenCombine(otherFuture, (a, b) -> a + b) |
两个任务都完成后合并结果 |
|
串行依赖(拍平) |
.thenCompose(s -> asyncMethod(s)) |
前一步结果作为下一步异步任务的输入 |
|
异常处理 |
.exceptionally(ex -> "默认值") |
捕获异常,返回降级值 |
|
等待任一完成 |
anyOf(cf1, cf2).thenAccept(…) |
竞速场景 |
直观的代码对比
❌ 传统 Future 写法(阻塞 + 难以编排)
Future<String> future = executor.submit(() -> "Hello");
String result = future.get(); // 阻塞等待
System.out.println(result);
✅ CompletableFuture 写法(异步 + 链式)
自动处理
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenAccept(System.out::println); 自动回调,不阻塞主线程
若主线程需要拿到返回结果
必须主线程拿到返回值,阻塞
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World");
主线程在这里会阻塞,直到异步任务全部完成,拿到最终字符串
String result = future.join();
System.out.println("主线程拿到了:" + result);


