You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:16:13