使用ProcessBuilder跨进程序列化对象时遭遇java.io.EOFException问题排查
解决Java ProcessBuilder子进程向父进程发送序列化对象时的EOFException问题
你的问题核心出在**builder.inheritIO()**这个调用上,这也是导致EOFException的根本原因。我来给你拆解一下问题和修复方案:
问题分析
inheritIO()方法会让子进程的标准输入、输出、错误流直接继承父进程的对应流——也就是说,子进程的System.out会直接输出到父进程的控制台,而不是通过Process.getInputStream()返回的管道流传递数据。这就导致父进程的ObjectInputStream根本读不到任何序列化数据,自然会抛出EOFException(因为流里没有数据,读到了末尾)。
另外,你的代码里还有几个可以优化的小细节:比如没有用try-with-resources管理流资源,以及if (process.isAlive())的判断其实意义不大(进程刚启动大概率是存活的,但数据可能还没写完,不过这不是核心问题)。
修复方案
1. 移除inheritIO()并单独处理错误流
首先要去掉builder.inheritIO(),让子进程的标准输出通过管道传给父进程。如果还想看到子进程的错误日志,可以单独把错误流重定向到父进程的控制台。
2. 用try-with-resources管理流
这能自动帮你关闭流,避免资源泄漏,代码也更简洁。
修改后的完整代码
DTO.java(完善hashCode和equals方法,不影响序列化但更规范)
import java.io.Serializable; public class DTO implements Serializable { private static final long serialVersionUID = 1L; private String name; public DTO(String name) { this.name = name; } public String getName() { return name; } @Override public int hashCode() { return name != null ? name.hashCode() : 0; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; DTO dto = (DTO) obj; return name != null ? name.equals(dto.name) : dto.name == null; } }
Parent.java(核心修改)
public class Parent { public static void main(String[] args) { try { new Parent().start(); } catch (Exception e) { e.printStackTrace(); } } public void start() throws IOException, InterruptedException, ClassNotFoundException { String classpath = System.getProperty("java.class.path"); String className = Child.class.getCanonicalName(); ProcessBuilder builder = new ProcessBuilder( "java", "-cp", classpath, className); // 移除inheritIO(),让子进程stdout通过管道传给父进程 // 单独把子进程的错误输出重定向到父进程控制台,方便调试 builder.redirectError(ProcessBuilder.Redirect.INHERIT); Process process = builder.start(); // 用try-with-resources自动管理ObjectInputStream try (ObjectInputStream input = new ObjectInputStream(process.getInputStream())) { DTO dto = (DTO) input.readObject(); System.out.println("接收到的DTO内容: " + dto.getName()); } // 等待子进程执行完成,获取退出码 int exitCode = process.waitFor(); System.out.println("子进程退出码: " + exitCode); } }
Child.java(优化流管理)
public class Child { public static void main(String[] args) throws IOException { DTO dto = new DTO("text"); // try-with-resources自动关闭流,无需手动调用close() try (ObjectOutputStream stream = new ObjectOutputStream(System.out)) { stream.writeObject(dto); stream.flush(); // 确保数据立即写入管道 } } }
为什么这样修改能解决问题?
- 去掉
inheritIO()后,子进程的System.out输出会被导向父进程可读取的管道,父进程的ObjectInputStream就能正确拿到序列化的DTO对象了。 - try-with-resources确保流会被自动关闭,避免了手动关闭可能出现的资源泄漏问题。
- 重定向错误流到控制台,让你能看到子进程的错误信息,方便后续调试。
process.waitFor()等待子进程完成,确保所有数据都被写入管道后再结束父进程的逻辑(不过readObject()本身会阻塞直到有数据,所以即使不调用waitFor也能读到数据,但waitFor能让你知道子进程的运行状态)。
内容的提问来源于stack exchange,提问作者Sameera Manorathna
相关产品推荐
相关产品推荐

