代码之家  ›  专栏  ›  技术社区  ›  James Adams

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

  •  68
  • James Adams  · 技术社区  · 16 年前

    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();
    }
    

    17 回复  |  直到 10 年前
        1
  •  91
  •   Aleksander Blomskøld    7 年前

    如果您使用的是Java8或更高版本,只需使用新的 waitFor with timeout :

    Process p = ...
    if(!p.waitFor(1, TimeUnit.MINUTES)) {
        //timeout - kill the process. 
        p.destroy(); // consider using destroyForcibly instead
    }
    
        2
  •  55
  •   erickson    7 年前
    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;
        }
      }  
    }
    
        3
  •  15
  •   Community Mohan Dere    8 年前

    遵循 answer by 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 !
    }
    
        4
  •  11
  •   Mogsdad    10 年前

    我使用建议的三种方法实现了这一点,这三种方法都附带了详细的代码示例(我是线程编程的新手,这些示例代码非常宝贵——如果只是用英语解释而没有代码的话,我仍然会挠头想怎么做)。

    我用三种方法实现了用于此目的的实用程序类,用于执行超时命令,如下所示:

    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();
            }
        }
    }
    

    #!/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线程类的方法效果最好,因为它在这两种情况下都给出了预期的结果。使用Executors的方法也按预期工作,但需要注意的是,在15000ms超时情况下,它似乎运行了两次命令(即,我看到了两次命令的输出)。使用sleep()方法的方法在5000ms超时情况下不会按预期使命令超时,并显示两次输出,但在15000ms超时情况下按预期运行命令。

        5
  •  5
  •   Rob Prime Rob Prime    16 年前

    对于所有使用executor框架的人:你们都忘记关闭executor了。因此,将其更改为以下内容:

    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();
    }
    

        6
  •  5
  •   Jean-François Savard    10 年前

    对于那些不能使用新Java8方法的人 waitFor(long timeout, TimeUnit unit) (因为它们在Android上,或者根本无法升级)您可以简单地从JDK源代码中删除它,并将其添加到您的UTIL文件中:

    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;
        }
    

    Process exitValue 方法从流程中删除。

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

    该方法返回一个布尔值,因此如果它返回false,则您知道需要手动终止该进程。

        7
  •  3
  •   Janus Troelsen    12 年前

    适用于小型应用程序的轻量级解决方案:

    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);
        }
    }
    
        8
  •  2
  •   Chris Ballance    16 年前

    以代理身份实现,如果需要超过阈值才能完成调用,则调用失败。

        9
  •  2
  •   Macke    16 年前

    尝试在单独的线程或事件队列(如果有)中使用计时器(或Sleep())。

        10
  •  2
  •   Neil Coffey    16 年前

    有很多方法可以做到这一点,但我会考虑使用一个执行器——它只是帮助您封装退出值或异常从线程返回到原始调用方。

        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;
        }
    

    我认为您无法绕过等待超时然后进程在调用destroy()之前终止的竞争条件。

        11
  •  2
  •   Peco    15 年前

    我还测试了worker的实现,效果非常好。在处理进程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();
                }
    
        12
  •  2
  •   xxnations    5 年前

    首先是一些背景信息,我在运行命令时遇到了超时的问题,因为我试图执行的程序在出现错误时不会打印任何调试或错误信息,并且只会自己在内部不断重试,导致进程停滞,因为重试时从未出现错误或输出流。

    process.exec() process.start()

    它将永远卡在这条线上,

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

    public boolean waitFor(long timeout,TimeUnit unit) 方法,它应该在指定的超时后“理想”超时,但在我的情况下,由于某种原因,它从未超时,可能是因为我以windows服务的形式运行应用程序(我已检查了用户权限和帐户上的所有内容,但没有帮助)。

    所以我尝试用下面的逻辑来实现它,在这里我们将使用 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));
                }
            }
        }
    }
    
        13
  •  0
  •   Valentin Jacquemin    16 年前

    您可以启动一个线程,该线程在您想要的时间内休眠,并在休眠后更改在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;
    }
    
        14
  •  0
  •   joran    14 年前

    这是流线

    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;
            }
    
    
    }
    
        15
  •  0
  •   xxg    10 年前

    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());
    
        16
  •  0
  •   sproger    10 年前

    如果使用Java8,我会选择AleksanderBlomskld回答,即p.waitFor(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
       }
    
        17
  •  0
  •   sabbibJAVA    9 年前

    我知道这是一个很老的帖子;我在一个类似的项目中需要一些帮助,所以我想我可能会给出一些我工作过的代码和那些工作过的代码。

    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;