reader.ready() 不能可靠判断字符流是否可读,仅表示缓冲区是否有数据可立即读取;误用会导致跳过数据或卡死,应改用 readline() 循环或 nio 非阻塞方案。

Reader.ready() 方法本身不能可靠判断“字符流是否可读”,尤其在网络或管道类流中。它只表示“当前缓冲区是否有数据可立即读取”,不是“后续会不会有数据”或“流是否已就绪”。误用 ready() 做循环条件(如 while(reader.ready()))极易导致跳过数据、空读、甚至逻辑卡死。
ready() 的真实含义和限制
ready() 是一个非阻塞查询方法,返回 true 仅说明:内部缓冲区中已有至少一个字符可被 read() 或 readLine() 立即取走,无需等待。
- 对 FileReader、StringReader 等本地资源流,ready() 大多返回 true(除非刚打开且未填充缓冲区)
- 对 InputStreamReader 包装的网络 Socket 流,ready() 往往返回 false —— 即使服务器已发数据,但因 TCP 接收窗口、Nagle 算法或应用层未及时填充 BufferedReader 缓冲区,JVM 无法保证“此刻有字符在缓冲区”
- readLine() 返回 null 只代表流已关闭且缓冲区彻底耗尽(EOF),不等于“没数据可读”;而 ready() 返回 false 也不等于“流断开”或“永远没数据”
常见误用及后果
以下写法是典型陷阱:
while (reader.ready()) { // ❌ 错误:可能一次都不进循环,即使后续有大量数据
String line = reader.readLine(); // 也可能返回 null(缓冲区空但连接未关)
}
- 若首次调用 ready() 就是 false,整个循环跳过,丢失所有后续到达的数据
- 若 readLine() 在中间某次返回 null(比如对方主动 close 输出流),但连接仍通,后续数据可能再也无法触发 ready() 为 true
- ready() 和 readLine() 混合使用顺序不当,容易造成状态错乱(例如先 readLine() 耗尽缓冲区,再查 ready() 得 false,却误判为“无数据”)
真正可用的非阻塞读取策略
要实现“不阻塞主线程、又能及时响应新数据”,应放弃依赖 ready() 做主控,改用以下方式:
- 用带超时的轮询 + try-catch 捕获阻塞:对 BufferedReader,可配合线程 sleep + readLine() 调用,并捕获 IOException 判断连接状态
- 改用 NIO 的 Selector + CharsetDecoder:用 java.nio.channels.SocketChannel 配合非阻塞模式,通过 select() 获取就绪通道,再 decode 字符,这才是真正的非阻塞字符流处理
- 用 Scanner 配合 hasNextLine()(注意:它底层仍可能阻塞):仅适用于可控场景(如本地文件或调试模拟),不推荐用于生产网络流
- 明确约定结束标记:让对端发送特定行(如 "END"),客户端持续 readLine() 直到匹配该标记,不依赖 ready() 或 EOF
简单安全的替代写法(适用于短连接或测试)
如果只是想避免无限阻塞,又不想引入 NIO,可这样写:
String line;
while ((line = reader.readLine()) != null) {
if (line.trim().isEmpty()) continue; // 忽略空行
process(line);
}
// 此时 reader 已到末尾或连接关闭
这个模式靠 readLine() 自身阻塞等待有效数据,不拼凑 ready(),逻辑清晰、不易出错。如需中断等待,应配合线程 interrupt 或使用支持 cancel 的异步 API(如 CompletableFuture + BufferedReader.lines())。










