线程池中需将受检异常转为运行时异常并保留中断状态:捕获interruptedioexception后调用interrupt()恢复标志,其他ioexception用uncheckedioexception包装;资源须实现autocloseable并用try-with-resources管理;配合uncaughtexceptionhandler和afterexecute做兜底处理。

线程池内部无法直接抛出受检异常(如 IOException、InterruptedException),因为 Runnable.run() 方法签名不声明任何异常。网络 I/O 中断常触发 InterruptedIOException(属于受检异常)或 InterruptedException,若不处理,会导致任务静默失败、资源泄漏、中断状态丢失。优雅处理的关键是:**不吞异常、不丢中断、不破契约、保留上下文**。
捕获并转为运行时异常,同时保留原始堆栈
在任务逻辑中主动包裹 I/O 操作,将受检异常包装为带因果链的 RuntimeException 子类(如 UncheckedIOException 或自定义业务异常):
- 用
catch (InterruptedIOException e)单独捕获,调用Thread.currentThread().interrupt()恢复中断标志——这是关键,否则上层 shutdown 流程可能收不到中断信号 - 再用
catch (IOException e)捕获其他 I/O 异常,统一包装成new UncheckedIOException(e)(JDK 自带)或自定义异常 - 避免只写
catch (Exception e),防止掩盖NullPointerException等逻辑错误
利用 try-with-resources 确保资源自动释放
所有网络资源(如 Socket、InputStream、HttpClient 连接)必须实现 AutoCloseable,才能在 try-with-resources 中安全关闭:
- 自定义网络工具类需显式实现
AutoCloseable,并在close()中处理 null 和重复关闭,并只抛IOException(不能抛其他受检异常) - 不要在
close()中吞掉异常;若关闭本身失败,应作为抑制异常(suppressed exception)附加到主异常上 - 例如:
try (Socket socket = new Socket(host, port); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { ... }
配合线程池的拒绝与钩子机制做兜底
单靠任务内捕获还不够,需结合线程池生命周期管理:
- 为
ThreadPoolExecutor设置ThreadFactory,在创建线程时统一设置UncaughtExceptionHandler,捕获未处理的RuntimeException并记录完整堆栈和任务上下文 - 重写
afterExecute(Runnable r, Throwable t):当t != null,说明任务以异常结束,可记录日志、触发告警、甚至标记任务重试标识 - 在线程池优雅关闭流程中(如
shutdownGracefully),使用@SneakyThrows包装awaitTermination,避免方法签名污染,同时确保中断发生时仍能穿透为RuntimeException并保留行号信息
区分中断场景,避免误判超时为故障
网络 I/O 中断不等于错误,可能是正常控制流(如请求取消、服务下线):
-
SocketTimeoutException是InterruptedIOException子类,但不是InterruptedException,不可靠恢复中断状态;应单独捕获,按业务策略决定重试或快速失败 -
UnknownHostException属于IOException,但本质是 DNS 解析失败,通常不可重试——应在发起网络调用前预解析并缓存InetAddress,而非在任务中被动捕获 - 收到
EOFException(常见于协议读取不完整)时,应终止当前连接、清理缓冲区,不继续读写,必要时重建新连接
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











