Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从Java运行外部程序,读取输出,允许中断_Java_Multithreading - Fatal编程技术网

从Java运行外部程序,读取输出,允许中断

从Java运行外部程序,读取输出,允许中断,java,multithreading,Java,Multithreading,我想从Java启动一个进程,读取其输出,并获取其返回代码。但是当它执行时,我希望能够取消它。我从启动流程开始: ProcessBuilder pb = new ProcessBuilder(args); pb.redirectErrorStream(true); Process proc = pb.start(); 如果调用proc.waitFor(),则在进程退出之前,我无法执行任何操作。所以我假设我需要这样做: while (true) { see if process has exi

我想从Java启动一个进程,读取其输出,并获取其返回代码。但是当它执行时,我希望能够取消它。我从启动流程开始:

ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
Process proc = pb.start();
如果调用proc.waitFor(),则在进程退出之前,我无法执行任何操作。所以我假设我需要这样做:

while (true) {
  see if process has exited
  capture some output from the process
  decide if I want to cancel it, and if so, cancel it
  sleep for a while
}

是这样吗?有人能给我一个在Java中如何做到这一点的例子吗?

下面是一个我认为您想要做的例子:

ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
Process proc = pb.start();

InputStream is = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);

String line;
int exit = -1;

while ((line = br.readLine()) != null) {
    // Outputs your process execution
    System.out.println(line);
    try {
        exit = proc.exitValue();
        if (exit == 0)  {
            // Process finished
        }
    } catch (IllegalThreadStateException t) {
        // The process has not yet finished. 
        // Should we stop it?
        if (processMustStop())
            // processMustStop can return true 
            // after time out, for example.
            proc.destroy();
    }
}

您可以改进它:-)我现在没有一个真正的环境来测试它,但您可以找到更多信息。

像这样的帮助器类可以做到这一点:

public class ProcessWatcher implements Runnable {

    private Process p;
    private volatile boolean finished = false;

    public ProcessWatcher(Process p) {
        this.p = p;
        new Thread(this).start();
    }

    public boolean isFinished() {
        return finished;
    }

    public void run() {
        try {
            p.waitFor();
        } catch (Exception e) {}
        finished = true;
    }

}
然后,您将完全按照您描述的方式实现您的循环:

Process p = Runtime.getRuntime().exec("whatever command");
ProcessWatcher pw = new ProcessWatcher(p);
InputStream output = p.getInputStream();
while(!pw.isFinished()) {
    processOutput(output);
    if(shouldCancel()) p.destroy();
    Thread.sleep(500);
}
根据需要销毁进程的条件,您可能希望在单独的线程中销毁进程。否则,您可能会在等待更多程序输出进行处理时阻塞,并且永远无法真正获得销毁它的选项


编辑:McDowell在下面的评论中100%正确,因此我将完成的变量设置为volatile。

是什么让您决定终止进程?异步事件(如用户输入)还是同步事件(如进程已完成您希望它完成的操作)?我猜是前者——用户的输入让您决定取消子流程

另外,您希望子流程产生多少输出?如果太多,那么如果您没有足够快地读取子流程的输出流,子流程可能会阻塞

您的情况可能会有所不同,但您可能至少需要两个不同的线程——一个用于决定是否取消进程,另一个用于处理子进程的输出

请在此处查看更多详细信息:

我建议退房以避免重新创建控制盘。它有一些很好的特性,比如在同步执行和异步执行之间进行选择,以及生成看门狗进程的标准解决方案,该解决方案可以在执行被卡住时帮助执行超时。

这方面如何(请参阅它在中的工作原理):

/**
*等待进程停止,并行记录其输出。
*@param process要等待的进程
*@return Stdout由进程生成
*@如果在其间中断,则抛出InterruptedException
*/
私有字符串waitFor(最终进程)抛出InterruptedException{
最终BufferedReader读取器=新BufferedReader(
新的InputStreamReader(process.getInputStream())
);
最终倒计时锁存器完成=新倒计时锁存器(1);
final StringBuffer stdout=新StringBuffer();
新线程(
新的可详细运行的(
新的可调用(){
@凌驾
public Void call()引发异常{
while(true){
最后一行字符串=reader.readLine();
如果(行==null){
打破
}
系统输出打印项次(“>>”+行);
标准输出追加(行);
}
完成。倒计时();
返回null;
}
},
假的
)
).start();
试一试{
process.waitFor();
}最后{
完成。等待();
IOUtils.安静地关闭(读卡器);
}
返回stdout.toString();
}

现在这个实现可以作为类从工件中获得。

至少应该将“finished”变量声明为“volatile”。这是我关心的问题;如果有一段时间没有输出呢?如果我调用BufferedReader.readLine()之类的东西,我不希望它永远挂起,直到有输出;我希望有机会取消。您可以使用以下命令创建一个新的可运行线程:public void run(){while(!pw.isFinished()){if(shouldCancel())p.destroy();Thread.sleep(500);}}}然后新建线程(myrunnable).start()。或者您的processOutput()方法可以检查output.available()只有当它不会阻塞时才读取。如果您这样做,那么您就不能使用readLine()-您必须自己处理行尾。或者,如果取消是在用户从GUI请求时进行的,那么您可以从处理用户事件的线程(例如按下按钮)调用process.destroy()。我已经在线程中运行,当需要取消时,该线程之外的人会设置我的一个字段。如果有一段时间没有输出呢?在有一行可以阅读之前,这不会一直挂在br.readLine()上吗?我不确定我的进程将发送多少输出。转到点。您可以创建一个单独的线程来分析进程是否必须停止。它可以与while循环并行运行,并在进程挂起一段时间(或任何其他情况)时销毁该进程。当进程完成后,您将从readLine()调用(刚刚确认)返回null。只有当进程不退出时,它才会挂起。我之所以这么做是因为我想要一个只使用核心JDK的“纯”程序。但你是对的,这种方式更好,解决了很多我在尝试创建自己的产品时不知道自己在注册的问题。
/**
 * Wait for the process to stop, logging its output in parallel.
 * @param process The process to wait for
 * @return Stdout produced by the process
 * @throws InterruptedException If interrupted in between
 */
private String waitFor(final Process process) throws InterruptedException {
    final BufferedReader reader = new BufferedReader(
        new InputStreamReader(process.getInputStream())
    );
    final CountDownLatch done = new CountDownLatch(1);
    final StringBuffer stdout = new StringBuffer();
    new Thread(
        new VerboseRunnable(
            new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    while (true) {
                        final String line = reader.readLine();
                        if (line == null) {
                            break;
                        }
                        System.out.println(">> " + line);
                        stdout.append(line);
                    }
                    done.countDown();
                    return null;
                }
            },
            false
        )
    ).start();
    try {
        process.waitFor();
    } finally {
        done.await();
        IOUtils.closeQuietly(reader);
    }
    return stdout.toString();
}