【问题标题】:Java: Run a Callable in a separate processJava:在单独的进程中运行 Callable
【发布时间】:2010-09-25 10:33:46
【问题描述】:

给定Callable<T> 的实例x,我如何在单独的进程中运行x,以便我可以重定向进程的标准输入和输出?例如,有没有办法从Callable 构建Process?是否有标准Executor 可以控制输入和输出?

[更新] Callable 在新进程而不是新线程中执行并不重要。我想要的是把Callable 实例放在“线束”中,这样​​我就可以控制它的标准输入/标准输出。 AFAIK,这需要一个新的流程。

【问题讨论】:

    标签: java multithreading process stdout stdin


    【解决方案1】:

    更笼统地说:

    给定一个使用全局变量 A 和 B 的 Callable 实例 x,我如何同时运行 x 以使 x 看到 A 和 B 的自定义值,而不是 A 和 B 的“原始”值?

    最好的答案是,不要使用全局变量。依赖注入之类的东西。扩展 Callable 并添加方法 setStdIn、setStdOut(如果需要,还可以添加 setStdErr)。

    我知道这不是您要寻找的答案,但我所看到的解决方案都需要一个新流程,而您将 Callable 放入新流程的唯一方法是更改​​代码Callable 所以它是可序列化的,或者提供一个类名,或者其他一些技巧,所以不要做会给你一个讨厌的、脆弱的解决方案的改变,只要做对*

    * “正确”是使用广泛接受的依赖注入模式来提供松散耦合。 YMMV

    更新:回应评论 1。这是您的新界面:

    import java.io.InputStream;
    import java.io.PrintStream;
    import java.util.concurrent.Callable;
    
    
    public interface MyCallable<V> extends Callable<V> {
      void setStdIn(InputStream in);
      void setStdOut(PrintStream out);  
    }
    

    你的任务应该是这样的:

    import java.io.InputStream;
    import java.io.PrintStream;
    
    
    public class CallableTask implements MyCallable<Object> {
    
        private InputStream in = System.in;
        private PrintStream out = System.out;
    
        public void setStdIn(InputStream in) {
            this.in = in;
        }
    
        public void setStdOut(PrintStream out) {
            this.out = out;
        }
    
        public Object call() throws Exception {
            out.write(in.read());
            return null;
        }
    

    }

    不需要过程。几乎可以肯定,任何解决方案(甚至是使用进程的解决方案)都需要以某种方式对 Callables 进行代码更改。这是最简单的(只需将 System.out 替换为 this.out)。

    【讨论】:

    • 如果不先回答这个问题,我将如何实施您的建议?
    • “依赖注入”并不是让整个问题消失的神奇小精灵,你知道的。
    • 这并不神奇,但全局变量被公认为代码异味。依赖注入是一个不错的选择。
    • 如果我真的需要一个额外的过程,因为我必须使用的库或多或少依赖于全局单例?
    • @Florian 你需要问自己几个问题:1)我是否在规避图书馆的意图?如果是这样,您可能会遇到其他问题,并且在升级库时您的解决方案可能会中断。 2)这个库是一个不错的选择吗?我可以在没有全局依赖的情况下使用其他东西吗? 3)还有其他方法吗?如果之后您仍然需要新流程,请查看 ProcessBuilder。
    【解决方案2】:

    stdin/stdout 可以重定向到整个系统,但我不确定它是否可以重定向到单个线程——你总是只是从 System. (不过,您可以让 System.out 转到另一个流,我已经用它来捕获堆栈跟踪,然后才有办法将跟踪作为字符串获取)

    您可以重定向标准输入/标准输出,运行可调用程序,然后将其重定向回来,但如果在重定向时其他任何东西使用 System.out,这也将转到您的新文件。

    如果你想走那条路,方法是 System.setIn() 和 System.setOut()(和 System.setErr())。

    【讨论】:

      【解决方案3】:

      努力使用上面的参数化、依赖注入或任何你想调用的方法来构造你的代码。避免静态,即使是那些装扮成单身人士或隐藏在糟糕图书馆中的人。

      如果您不想删除 System.in/out/err 的使用,那么您很幸运。这些可以使用System.set(In/Out/Err) 全局设置。为了能够为各个线程进行不同的流式传输,请设置一个使用ThreadLocal 来查找要委托给的目标的实现。线程局部变量通常是邪恶的,但在不幸的情况下可能很有用。

      从澄清来看,原发帖人似乎不需要创建单独的流程。

      【讨论】:

        【解决方案4】:

        看看 ProcessBuilder 类,它让您可以选择捕获它启动的进程的标准输出和标准错误。

        您可以创建一个运行 Callable 的 Main 类,然后将其作为另一个带有 ProcessBuilder 的 jvm 启动。 Main 类可以接受您的可调用对象的类名作为命令行输入参数,并使用 Class.forName() 和 Class.newInstance() 加载它。

        如果您希望它运行特定的可调用实例,另一种方法是在启动另一个进程之前将该实例序列化为一个文件(这将是两个进程之间非常粗略的通信形式)。

        问题是,你为什么要这样做?你不能在另一个线程中运行你的可调用对象吗? java.util.concurrent 有一些有用的线程池,仅此而已。

        【讨论】:

        • ProcessBuilder,AFAICT,需要命令行字符串而不是 Callable。我肯定想调用实例而不是基于类名创建新实例。至于进程与线程,见上文。
        • 我的意思是您将 java.exe 作为命令提供给 ProcessBUilder,而对于 Main 类,您提供一个运行可调用对象的包装器。当然,您需要序列化实例并将文件名作为参数传递给您的主类。或者只是使用 System.setIn()/out() 等。
        猜你喜欢
        • 2015-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-12
        相关资源
        最近更新 更多