如何在使用 Java 的 Runtime.exec()时添加超时值?

我有一个用于在本地主机上执行命令的方法。我想给这个方法添加一个超时参数,这样如果被调用的命令没有在合理的时间内完成,这个方法将返回一个错误代码。下面是目前为止看起来没有超时的情况:

public static int executeCommandLine(final String commandLine,
final boolean printOutput,
final boolean printError)
throws IOException, InterruptedException
{
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(commandLine);


if (printOutput)
{
BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
System.out.println("Output:  " + outputReader.readLine());
}


if (printError)
{
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
System.out.println("Error:  " + errorReader.readLine());
}


return process.waitFor();
}

有人能为我提供一个实现超时参数的好方法吗?

99415 次浏览

作为委托实现,如果调用超过您的阈值才能完成,则调用失败。

如果有可用的 Timer (或 Sleep ()) ,请尝试在单独的线程或事件队列中使用它。

您可以启动一个线程,该线程可以按照您希望的时间睡眠,并在睡眠之后更改您在 ExecuteCommandLine 方法中循环的布尔值。

类似的东西(没有测试也没有编译,这个解决方案是一个原型,你应该重构它,如果它适合你的需要) :

public static int executeCommandLine(final String commandLine,
final boolean printOutput,
final boolean printError)
throws IOException, InterruptedException
{
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(commandLine);


if (printOutput)
{
BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
System.out.println("Output:  " + outputReader.readLine());
}


if (printError)
{
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
System.out.println("Error:  " + errorReader.readLine());
}


ret = -1;
final[] b = {true};
new Thread(){
public void run(){
Thread.sleep(2000); //to adapt
b[0] = false;
}
}.start();
while(b[0])
{
ret = process.waitFor();
}


return ret;
}
public static int executeCommandLine(final String commandLine,
final boolean printOutput,
final boolean printError,
final long timeout)
throws IOException, InterruptedException, TimeoutException {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(commandLine);
/* Set up process I/O. */
...
Worker worker = new Worker(process);
worker.start();
try {
worker.join(timeout);
if (worker.exit != null)
return worker.exit;
else
throw new TimeoutException();
} catch(InterruptedException ex) {
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
} finally {
process.destroyForcibly();
}
}


private static class Worker extends Thread {
private final Process process;
private Integer exit;
private Worker(Process process) {
this.process = process;
}
public void run() {
try {
exit = process.waitFor();
} catch (InterruptedException ignore) {
return;
}
}
}

有多种方法可以做到这一点,但是我会考虑使用 Execator ——它只是帮助您将退出值或异常从线程传递回原始调用者。

    final Process p = ...
Callable<Integer> call = new Callable<Integer>() {
public Integer call() throws Exception {
p.waitFor();
return p.exitValue();
}
};
Future<Integer> ft = Executors.newSingleThreadExecutor().submit(call);
try {
int exitVal = ft.get(2000L, TimeUnit.MILLISECONDS);
return exitVal;
} catch (TimeoutException to) {
p.destroy();
throw to;
}

我认为您不能绕过这样的竞态条件,即等待超时,然后进程在您调用 delete ()之前终止。

我使用了三种方法来实现它,这三种方法都是通过详细的代码示例得到的(我是一个线程编程的新手,这些示例代码是无价的——如果只是用英语解释而没有代码,我仍然会摸不着头脑)。

我使用三个方法实现了用于执行带有超时的命令的实用工具类,如下所示:

package com.abc.network.lifecycle.util;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
* Utility class for performing process related functions such as command line processing.
*/
public class ProcessUtility
{


static Log log = LogFactory.getLog(ProcessUtility.class);


/**
* Thread class to be used as a worker
*/
private static class Worker
extends Thread
{
private final Process process;
private Integer exitValue;


Worker(final Process process)
{
this.process = process;
}


public Integer getExitValue()
{
return exitValue;
}


@Override
public void run()
{
try
{
exitValue = process.waitFor();
}
catch (InterruptedException ignore)
{
return;
}
}
}


/**
* Executes a command.
*
* @param command
* @param printOutput
* @param printError
* @param timeOut
* @return
* @throws java.io.IOException
* @throws java.lang.InterruptedException
*/
public static int executeCommandWithExecutors(final String command,
final boolean printOutput,
final boolean printError,
final long timeOut)
{
// validate the system and command line and get a system-appropriate command line
String massagedCommand = validateSystemAndMassageCommand(command);


try
{
// create the process which will run the command
Runtime runtime = Runtime.getRuntime();
final Process process = runtime.exec(massagedCommand);


// consume and display the error and output streams
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
outputGobbler.start();
errorGobbler.start();


// create a Callable for the command's Process which can be called by an Executor
Callable<Integer> call = new Callable<Integer>()
{
public Integer call()
throws Exception
{
process.waitFor();
return process.exitValue();
}
};


// submit the command's call and get the result from a
Future<Integer> futureResultOfCall = Executors.newSingleThreadExecutor().submit(call);
try
{
int exitValue = futureResultOfCall.get(timeOut, TimeUnit.MILLISECONDS);
return exitValue;
}
catch (TimeoutException ex)
{
String errorMessage = "The command [" + command + "] timed out.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
catch (ExecutionException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an execution error.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
}
catch (InterruptedException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
catch (IOException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
}


/**
* Executes a command.
*
* @param command
* @param printOutput
* @param printError
* @param timeOut
* @return
* @throws java.io.IOException
* @throws java.lang.InterruptedException
*/
public static int executeCommandWithSleep(final String command,
final boolean printOutput,
final boolean printError,
final long timeOut)
{
// validate the system and command line and get a system-appropriate command line
String massagedCommand = validateSystemAndMassageCommand(command);


try
{
// create the process which will run the command
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(massagedCommand);


// consume and display the error and output streams
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
outputGobbler.start();
errorGobbler.start();


// run a thread which will set a flag once it has slept for the timeout period
final boolean[] flags = { true };
new Thread()
{
@Override
public void run()
{
try
{
Thread.sleep(timeOut);
}
catch (InterruptedException ex)
{
String errorMessage = "Timeout loop thread unexpectedly interrupted.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
flags[0] = false;
}
}.start();


// execute the command and wait
int returnValue = -1;
while (flags[0] && (returnValue < 0))
{
returnValue = process.waitFor();
}


// if the command timed out then log it
if (returnValue < 0)
{
log.warn("The command [" + command + "] did not complete before the timeout period expired (timeout: " +
timeOut + " ms)");
}


return returnValue;
}
catch (InterruptedException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
catch (IOException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
}


/**
* Executes a command.
*
* @param command
* @param printOutput
* @param printError
* @param timeOut
* @return
* @throws java.io.IOException
* @throws java.lang.InterruptedException
*/
public static int executeCommandWithWorker(final String command,
final boolean printOutput,
final boolean printError,
final long timeOut)
{
// validate the system and command line and get a system-appropriate command line
String massagedCommand = validateSystemAndMassageCommand(command);


try
{
// create the process which will run the command
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(massagedCommand);


// consume and display the error and output streams
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
outputGobbler.start();
errorGobbler.start();


// create and start a Worker thread which this thread will join for the timeout period
Worker worker = new Worker(process);
worker.start();
try
{
worker.join(timeOut);
Integer exitValue = worker.getExitValue();
if (exitValue != null)
{
// the worker thread completed within the timeout period
return exitValue;
}


// if we get this far then we never got an exit value from the worker thread as a result of a timeout
String errorMessage = "The command [" + command + "] timed out.";
log.error(errorMessage);
throw new RuntimeException(errorMessage);
}
catch (InterruptedException ex)
{
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
}
}
catch (InterruptedException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
catch (IOException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
}


/**
* Validates that the system is running a supported OS and returns a system-appropriate command line.
*
* @param originalCommand
* @return
*/
private static String validateSystemAndMassageCommand(final String originalCommand)
{
// make sure that we have a command
if (originalCommand.isEmpty() || (originalCommand.length() < 1))
{
String errorMessage = "Missing or empty command line parameter.";
log.error(errorMessage);
throw new RuntimeException(errorMessage);
}


// make sure that we are running on a supported system, and if so set the command line appropriately
String massagedCommand;
String osName = System.getProperty("os.name");
if (osName.equals("Windows XP"))
{
massagedCommand = "cmd.exe /C " + originalCommand;
}
else if (osName.equals("Solaris") || osName.equals("SunOS") || osName.equals("Linux"))
{
massagedCommand = originalCommand;
}
else
{
String errorMessage = "Unable to run on this system which is not Solaris, Linux, or Windows XP (actual OS type: \'" +
osName + "\').";
log.error(errorMessage);
throw new RuntimeException(errorMessage);
}


return massagedCommand;
}
}

我创建了一个类来使用和显示来自命令(取自 http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4)的输出和错误流:

package com.abc.network.lifecycle.util;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
* Utility thread class which consumes and displays stream input.
*
* Original code taken from http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
*/
class StreamGobbler
extends Thread
{
static private Log log = LogFactory.getLog(StreamGobbler.class);
private InputStream inputStream;
private String streamType;
private boolean displayStreamOutput;


/**
* Constructor.
*
* @param inputStream the InputStream to be consumed
* @param streamType the stream type (should be OUTPUT or ERROR)
* @param displayStreamOutput whether or not to display the output of the stream being consumed
*/
StreamGobbler(final InputStream inputStream,
final String streamType,
final boolean displayStreamOutput)
{
this.inputStream = inputStream;
this.streamType = streamType;
this.displayStreamOutput = displayStreamOutput;
}


/**
* Consumes the output from the input stream and displays the lines consumed if configured to do so.
*/
@Override
public void run()
{
try
{
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = null;
while ((line = bufferedReader.readLine()) != null)
{
if (displayStreamOutput)
{
System.out.println(streamType + ">" + line);
}
}
}
catch (IOException ex)
{
log.error("Failed to successfully consume and display the input stream of type " + streamType + ".", ex);
ex.printStackTrace();
}
}
}

我创建了一个测试命令,大约需要10秒钟来完成:

#!/bin/bash
sleep 10
echo 'TEST COMMAND RAN OK'

然后,我创建了一个测试程序来测试三个不同的方法,调用每个方法的超时值为5秒(命令应该失败) ,超时值为15秒(命令应该成功) :

package com.abc.network.lifecycle.util;


public class ProcessUtilityTester
{


/**
* @param args
*/
public static void main(final String[] args)
{
try
{
String command = args[0];
int exitValue = -1;
System.out.println("\n\n5000ms timeout With Executors:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithExecutors(command, true, true, 5000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n5000ms timeout With Sleep:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithSleep(command, true, true, 5000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n5000ms timeout With Worker:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithWorker(command, true, true, 5000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n15000ms timeout With Executors:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithExecutors(command, true, true, 15000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n15000ms timeout With Sleep:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithSleep(command, true, true, 15000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n15000ms timeout With Worker:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithWorker(command, true, true, 15000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.exit(0);
}
}


}

下面是我在运行测试程序时看到的:

5000ms timeout With Executors:
May 1, 2009 1:55:19 AM com.abc.network.lifecycle.util.ProcessUtility executeCommandWithExecutors
SEVERE: The command [/tmp/testcmd.sh] timed out.
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
at java.util.concurrent.FutureTask.get(FutureTask.java:91)
at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithExecutors(ProcessUtility.java:179)
at com.abc.network.lifecycle.util.ProcessUtilityTester.main(ProcessUtilityTester.java:19)
java.lang.RuntimeException: The command [/tmp/testcmd.sh] timed out.
at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithExecutors(ProcessUtility.java:186)
at com.abc.network.lifecycle.util.ProcessUtilityTester.main(ProcessUtilityTester.java:19)
Caused by: java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
at java.util.concurrent.FutureTask.get(FutureTask.java:91)
at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithExecutors(ProcessUtility.java:179)
... 1 more


Exit value:-1




5000ms timeout With Sleep:
OUTPUT>TEST COMMAND RAN OK
OUTPUT>TEST COMMAND RAN OK


Exit value:0




5000ms timeout With Worker:
May 1, 2009 1:55:34 AM com.abc.network.lifecycle.util.ProcessUtility executeCommandWithWorker
SEVERE: The command [/tmp/testcmd.sh] timed out.
java.lang.RuntimeException: The command [/tmp/testcmd.sh] timed out.
at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithWorker(ProcessUtility.java:338)
at com.abc.network.lifecycle.util.ProcessUtilityTester.main(ProcessUtilityTester.java:47)


Exit value:-1




15000ms timeout With Executors:
OUTPUT>TEST COMMAND RAN OK
OUTPUT>TEST COMMAND RAN OK


Exit value:0




15000ms timeout With Sleep:
OUTPUT>TEST COMMAND RAN OK


Exit value:0




15000ms timeout With Worker:
OUTPUT>TEST COMMAND RAN OK


Exit value:0

因此,根据我所知道的,使用 Worker 线程类的方法效果最好,因为它在两种情况下都给出了预期的结果。在15000ms 超时情况下(即我看到命令的输出两次) ,使用 Execators 的方法也可以正常工作,但需要注意的是它似乎运行了两次命令。使用 sleep ()方法的方法不会像在5000ms 超时情况下预期的那样超时命令,并且会两次显示输出,但是会像在15000ms 超时情况下预期的那样运行命令。

对于每个使用执行器框架的人来说: 你们都忘了关闭执行器。因此,将它改为以下内容:

ExecutorService service = Executors.newSingleThreadExecutor();
try {
Future<Integer> ft = service.submit(call);
try {
int exitVal = ft.get(2000L, TimeUnit.MILLISECONDS);
return exitVal;
} catch (TimeoutException to) {
p.destroy();
throw to;
}
}
finally {
service.shutdown();
}

如果您不这样做,您的程序将保持一个活动的非守护进程线程,确保您的程序将永远不会退出,直到您调用 System.exit

我还测试了工作人员的实现,并且工作得非常出色。在处理进程 io 下,我添加了线程来处理 stde 和 stdo。如果工作线程超时,我也退出 IO 线程。

Process p = Runtime.getRuntime().exec(cmd.trim());


//setup error and output stream threads
CommandStreamThread eStream = new CommandStreamThread(p.getErrorStream(), "STDE");
CommandStreamThread oStream = new CommandStreamThread(p.getInputStream(), "STDO");


// kick them off
eStream.start();
oStream.start();


//setup a worker thread so we can time it out when we need
CommandWorkerThread worker=new CommandWorkerThread(p);
worker.start();


try {
worker.join(this.getTimeout());
if (worker.getExit() != null)
return worker.getExit();
else
throw new TimeoutException("Timeout reached:"+this.getTimeout()+" ms");
} catch(InterruptedException ex) {
eStream.interrupt();
oStream.interrupt();
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
} finally {
p.destroy();
}

这是 StreamThread

public class CommandStreamThread extends Thread{
private InputStream iStream;
private String cPrompt;


CommandStreamThread (InputStream is, String cPrompt)
{
this.iStream = is;
this.cPrompt = cPrompt;
}


public void run()
{
try
{
InputStreamReader streamReader= new InputStreamReader(this.iStream);
BufferedReader reader = new BufferedReader(streamReader);




String linesep=System.getProperty("line.separator");
String line=null;
while ((line=reader.readLine())!=null){
System.out.println(line);
//Process the next line seperately in case this is EOF is not preceded by EOL
int in;
char[] buffer=new char[linesep.length()];
while ( (in = reader.read(buffer)) != -1){
String bufferValue=String.valueOf(buffer, 0, in);
System.out.print(bufferValue);
if (bufferValue.equalsIgnoreCase(linesep))
break;
}
}


//Or the easy way out with commons utils!
//IOUtils.copy(this.iStream, System.out);




} catch (Exception e){
e.printStackTrace();
}
}


public InputStream getIStream() {
return iStream;
}


public void setIStream(InputStream stream) {
iStream = stream;
}


public String getCPrompt() {
return cPrompt;
}


public void setCPrompt(String prompt) {
cPrompt = prompt;
}




}

针对小型应用的轻量级解决方案:

public class Test {
public static void main(String[] args) throws java.io.IOException, InterruptedException {
Process process = new ProcessBuilder().command("sleep", "10").start();


int i=0;
boolean deadYet = false;
do {
Thread.sleep(1000);
try {
process.exitValue();
deadYet = true;
} catch (IllegalThreadStateException e) {
System.out.println("Not done yet...");
if (++i >= 5) throw new RuntimeException("timeout");
}
} while (!deadYet);
}
}

遵循 Erickson 回答,我创建了一种更通用的方法来做同样的事情。

public class ProcessWithTimeout extends Thread
{
private Process m_process;
private int m_exitCode = Integer.MIN_VALUE;


public ProcessWithTimeout(Process p_process)
{
m_process = p_process;
}


public int waitForProcess(int p_timeoutMilliseconds)
{
this.start();


try
{
this.join(p_timeoutMilliseconds);
}
catch (InterruptedException e)
{
this.interrupt();
}


return m_exitCode;
}


@Override
public void run()
{
try
{
m_exitCode = m_process.waitFor();
}
catch (InterruptedException ignore)
{
// Do nothing
}
catch (Exception ex)
{
// Unexpected exception
}
}
}

现在,你要做的就是:

Process process = Runtime.getRuntime().exec("<your command goes here>");
ProcessWithTimeout processWithTimeout = new ProcessWithTimeout(process);
int exitCode = processWithTimeout.waitForProcess(5000);


if (exitCode == Integer.MIN_VALUE)
{
// Timeout
}
else
{
// No timeout !
}

如果您使用的是 Java8或更高版本,那么您可以简单地使用新的 等待暂停:

Process p = ...
if(!p.waitFor(1, TimeUnit.MINUTES)) {
//timeout - kill the process.
p.destroy(); // consider using destroyForcibly instead
}

Apache Commons Exec 可以帮助您完成这项工作。

参见 http://commons.apache.org/proper/commons-exec/tutorial.html

String line = "your command line";
CommandLine cmdLine = CommandLine.parse(line);
DefaultExecutor executor = new DefaultExecutor();
ExecuteWatchdog watchdog = new ExecuteWatchdog(60000);
executor.setWatchdog(watchdog);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
executor.setStreamHandler(streamHandler);
int exitValue = executor.execute(cmdLine);
System.out.println(exitValue);
System.out.println(outputStream.toString());

如果使用 Java8,我会选择 Aleksander Blomskøld 的回答,例如 p.wait For (1,TimeUnit.MINUTE)

否则,如果你使用 Java 6/7和 Swing,那么你可以使用 SwingWorker:

   final Process process = ...
SwingWorker<Integer, Integer> sw = new SwingWorker<>() {
@Override
protected Integer doInBackground() throws Exception {
process.waitFor();
return process.exitValue();
}
};
sw.execute();
int exitValue = sw.get(1, TimeUnit.SECONDS);
if (exitValue == 0) {
//everything was fine
} else {
//process exited with issues
}

首先是一些背景信息,我遇到了一个问题,在运行命令时有一个超时,因为我尝试执行的程序永远不会打印任何调试或错误信息,如果出现错误,将只是继续内部重试本身导致进程卡住,因为从来没有一个错误或输出流时,它重试。

所以在 process.exec()或者 process.start()之后,

它会永远卡在这条线上,

BufferedReader input = new BufferedReader(newInputStreamReader(process.getInputStream()));

根据 java 1.8的 public boolean waitFor(long timeout,TimeUnit unit)方法,它应该在指定的超时后“理想地”超时,但在我的情况下,由于某种原因,它从来没有超时,可能是因为我运行的应用程序作为一个窗口服务(我已经检查了用户权限和帐户上的一切,但它没有帮助)。

因此,我尝试用下面的逻辑来实现它,在这个逻辑中,我们将使用 input.ready()和一个超时标志来检查输入流。这个简单的解决方案与其他所有的方案相比,简直是神奇无比。

密码:

public boolean runCommand() throws IOException, InterruptedException, Exception {
StringBuilder rawResponse = new StringBuilder();
System.out.println("Running Command " + Arrays.toString(command));
ProcessBuilder processBuilder = new ProcessBuilder(Arrays.asList(command));
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start(); //Executing the process
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
waitForTimeout(input, process); //Waiting for Timout
String line;
while ((line = input.readLine()) != null) {
rawResponse.append(line).append("\n");
}
return true;
}




//Timeout method
private void waitForTimeout(BufferedReader input, Process process) throws InterruptedException, Exception {
int timeout = 5;
while (timeout > 0) {
if (!process.isAlive() || input.ready()) {
break;
} else {
timeout--;
Thread.sleep(1000);
if (timeout == 0 && !input.ready()) {
destroyProcess(process);
throw new Exception("Timeout in executing the command "+Arrays.toString(command));
}
}
}
}

对于那些不能使用新的 Java 8方法 waitFor(long timeout, TimeUnit unit)的人(因为他们在 Android 上或者根本不能升级) ,你可以简单地从 JDK 源代码中删除它,然后将它添加到 utils 文件的某个地方:

public boolean waitFor(long timeout, TimeUnit unit, final Process process)
throws InterruptedException
{
long startTime = System.nanoTime();
long rem = unit.toNanos(timeout);


do {
try {
process.exitValue();
return true;
} catch(IllegalThreadStateException ex) {
if (rem > 0)
Thread.sleep(
Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
}
rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
} while (rem > 0);
return false;
}

我从 JDK8源代码中对原始方法所做的唯一更改是添加了 Process参数,这样我们就可以从进程中调用 exitValue方法。

如果进程尚未终止,则 exitValue方法将直接尝试返回或抛出 IllegalThreadStateException。在这种情况下,我们等待接收到的超时并终止。

该方法返回一个布尔值,所以如果它返回 false,那么您就知道需要手动终止该进程。

这种方法似乎比上面提到的任何方法都要简单(当然,可以直接调用 wait For)。

我知道这是真正的老职位,我需要一些类似的项目帮助,所以我想我可能会给我的一些代码,我的工作和一些工作。

long current = System.currentTimeMillis();


ProcessBuilder pb  = new ProcessBuilder(arguments);
try{
pb.redirectErrorStream(true);
process = pb.start();
int c ;
while((c = process.getInputStream().read()) != -1 )
if(System.currentTimeMillis() - current < timeOutMilli)
result += (char)c;
else throw new Exception();
return result.trim();
}catch(Exception e){
e.printStackTrace();
}
return result;

希望这对未来有所帮助