方案 1
使用 execute
+ try-catch
记录异常
import java.util.concurrent.*;
public class ThreadPoolExceptionDemo {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new ThreadFactory() {
private int count = 1;
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "custom-thread-" + count++);
}
});
executor.execute(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 正在执行任务");
throw new RuntimeException("任务异常");
} catch (Exception e) {
System.err.println("线程 " + Thread.currentThread().getName() + " 捕获异常: " + e.getMessage());
e.printStackTrace();
}
});
executor.shutdown();
}
}
方案 2
使用 submit
+ Future
submit()
方法返回 Future
,可以通过 get()
方法捕获异常:
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<?> future = executor.submit(() -> {
System.out.println(Thread.currentThread().getName() + " 正在执行任务");
throw new RuntimeException("任务异常");
});
try {
future.get(); // get() 会抛出 ExecutionException
} catch (InterruptedException | ExecutionException e) {
System.err.println("线程 " + Thread.currentThread().getName() + " 捕获异常: " + e.getCause().getMessage());
e.printStackTrace();
}
executor.shutdown();
}
注意
get()
方法会阻塞主线程直到任务完成。ExecutionException
的getCause()
方法可以获取原始异常。
方案 3
自定义 UncaughtExceptionHandler
可以为线程设置 UncaughtExceptionHandler
,当 Runnable
没有捕获异常时,ThreadPoolExecutor
也不会吞掉异常:
public class ThreadPoolWithExceptionHandler {
public static void main(String[] args) {
ThreadFactory threadFactory = r -> {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler((thread, throwable) -> {
System.err.println("线程 " + thread.getName() + " 发生异常: " + throwable.getMessage());
throwable.printStackTrace();
});
return t;
};
ExecutorService executor = new ThreadPoolExecutor(
2, 4, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
threadFactory
);
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 正在执行任务");
throw new RuntimeException("任务异常");
});
executor.shutdown();
}
}
方案 4
重写 afterExecute
方法
如果你要在 ThreadPoolExecutor
内部直接处理异常,可以继承 ThreadPoolExecutor
并重写 afterExecute()
:
class CustomThreadPoolExecutor extends ThreadPoolExecutor {
public CustomThreadPoolExecutor(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 {
((Future<?>) r).get(); // 获取任务结果,捕获异常
} catch (InterruptedException | ExecutionException e) {
t = e.getCause();
}
}
if (t != null) {
System.err.println("线程 " + Thread.currentThread().getName() + " 发生异常: " + t.getMessage());
t.printStackTrace();
}
}
}
public class ThreadPoolAfterExecuteDemo {
public static void main(String[] args) {
ThreadPoolExecutor executor = new CustomThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
executor.submit(() -> {
System.out.println(Thread.currentThread().getName() + " 正在执行任务");
throw new RuntimeException("任务异常");
});
executor.shutdown();
}
}
结论
方案 | 适用场景 | 缺点 |
---|---|---|
try-catch 手动处理 | 适用于 execute() | 代码侵入性强,所有任务都要加 try-catch |
Future.get() 捕获异常 | 适用于 submit() | get() 会阻塞主线程 |
UncaughtExceptionHandler | 适用于 execute() | 不能捕获 submit() 提交的异常 |
afterExecute() | 适用于 execute() 和 submit() | 需要继承 ThreadPoolExecutor |
推荐:
- 任务内部
try-catch
适用于execute()
Future.get()
适用于submit()
- 统一异常处理建议使用
afterExecute()
或UncaughtExceptionHandler