Làm cách nào để thêm giá trị thời gian chờ khi sử dụng Java's Runtime.exec ()?


76

Tôi có một phương pháp tôi đang sử dụng để thực thi một lệnh trên máy chủ cục bộ. Tôi muốn thêm một tham số thời gian chờ vào phương thức để nếu lệnh được gọi không kết thúc trong một khoảng thời gian hợp lý, phương thức sẽ trả về mã lỗi. Đây là những gì nó trông giống như cho đến nay, không có khả năng hết thời gian:

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

Bất cứ ai có thể đề xuất một cách tốt để tôi triển khai thông số thời gian chờ không?


Tôi coi đây là một ví dụ đơn giản hóa. Bạn biết điều này sẽ chặn nếu bộ đệm cho đầu ra tiêu chuẩn hoặc lỗi tiêu chuẩn chạy đầy. Bạn đã đọc chúng không đồng bộ, cả hai song song.
Michael Piefel

@MichaelPiefel Đúng là như vậy. Vì vậy, tôi đang bắt đầu một Chủ đề mới cho những thứ này, nhưng câu hỏi là khi quá trình chết, người ta có phải giết các chủ đề đó không?
mmm,

Câu trả lời:


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

Tôi đã thử điều này, nhưng tôi nhận được giá trị worker.exit luôn null.
vishal


10
Đừng chỉ đặt ra một khối mã. Vui lòng cung cấp giải thích.
Stealth Rabbi

Có lẽ tôi không hiểu Thread.join(long time)hoặc Thread.wait(long time), Thread.join(long time)có thể gọi. Làm thế nào để điều này dừng quá trình đang thực hiện nếu thời gian chờ đã hết? Tôi đã chạy thử nghiệm bằng cách sử dụng ở trên, trong đó tôi in thời gian hiện tại stdout, chạy quy trình và sau đó in lại thời gian. Quá trình này mất khoảng 3 giây để thực hiện. Quá trình luôn hoàn tất sau khi khoảng thời gian chờ đã trôi qua, mặc dù tôi đã chỉ định thời gian chờ là 1s và mong đợi nhận được a TimeoutException.
Agi Hammerthief

@AgiHammerthief Tôi đang dựa vào Process.destroy()để giết các quy trình chưa hoàn thành sau thời gian chờ. Tôi sẽ làm một số thử nghiệm. Bạn đang chạy hệ điều hành nào? Bạn đã chạy thử nghiệm của mình bên trong IDE hay từ một trình bao?
erickson

84

Nếu bạn đang sử dụng Java 8 trở lên, bạn có thể chỉ cần sử dụng waitFor mới hoặc hết thời gian :

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

15

Sau câu trả lời của erickson , tôi đã tạo ra một cách chung chung hơn để làm điều tương tự.

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

Bây giờ, tất cả những gì bạn phải làm là như sau:

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

Rất hữu dụng. Tôi đã sử dụng khuôn này (vì nó cho phép tôi giữ nguyên luồng tiêu thụ luồng của mình). Tuy nhiên, tôi đã cập nhật waitForProcesstài khoản cho các gián đoạn giả ...: paste.ubuntu.com/9898052
xem

1
tôi sẽ thay đổi p_timeoutMillisecondsthành dài. ví dụpublic int waitForProcess(long timeoutMilliseconds)
Jossef Harush.

Tôi không nghĩ là tốt khi sử dụng Interger.MIN_VALUE để báo hiệu thời gian chờ
Zoltán Haindrich

11

Tôi đã triển khai điều này bằng cách sử dụng ba cách tiếp cận được đề xuất đi kèm với ví dụ mã chi tiết (Tôi là người mới làm quen với lập trình luồng và những mã ví dụ này là vô giá - tôi vẫn sẽ vò đầu bứt tai không biết làm thế nào nếu nó chỉ được giải thích bằng tiếng Anh không có mã).

Tôi đã triển khai lớp tiện ích mà tôi đang sử dụng cho việc này với ba phương thức để thực thi một lệnh có thời gian chờ như sau:

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

Tôi đã tạo một lớp để sử dụng và hiển thị các luồng đầu ra và lỗi từ một lệnh (lấy từ 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();
        }
    }
}

Tôi đã tạo một lệnh kiểm tra mất khoảng 10 giây để hoàn thành:

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

Sau đó, tôi đã tạo một chương trình thử nghiệm để kiểm tra ba phương pháp khác nhau, gọi mỗi phương thức với giá trị thời gian chờ là 5 giây (lệnh sẽ thất bại) và với giá trị thời gian chờ là 15 giây (lệnh sẽ thành công):

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

}

Đây là những gì tôi thấy khi chạy chương trình thử nghiệm:

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

Vì vậy, từ những gì tôi có thể cho biết phương pháp sử dụng lớp luồng Worker hoạt động tốt nhất, trong đó nó mang lại kết quả mong đợi trong cả hai trường hợp. Cách tiếp cận sử dụng Executor cũng hoạt động như mong đợi, với cảnh báo rằng nó dường như đang chạy lệnh hai lần trong trường hợp timout 15000ms (tức là tôi thấy đầu ra cho lệnh hai lần). Cách tiếp cận sử dụng phương thức sleep () không hết thời gian lệnh như mong đợi trong trường hợp thời gian chờ 5000ms và hiển thị đầu ra hai lần, nhưng chạy lệnh như mong đợi trong trường hợp thời gian chờ 15000ms.


Tôi không nghĩ rằng bạn nên quay lại sau khi chờ mã thoát quá trình bị gián đoạn. Điều này sẽ lặp lại cho đến khi bạn nhận được mã thoát thành công.
Pawel Veselov

Tôi có thể hỏi tại sao bạn sử dụng cmd.exe thay vì gọi trực tiếp tiến trình 'exe không?
PhilW

Tôi đã thử điều này nhưng nó luôn ném ra ngoại lệ Timeout stackoverflow.com/questions/23756326/…
vishal

@JamesAdams Tôi đã phải thêm Windows 7 vào danh sách hệ điều hành để nó chạy. Kỳ lạ là công nhân đang chạy hai lần cho tôi. Chạy các lệnh bên ngoài từ java có vẻ quá khó hiểu đối với tôi.
localhost

5

Đối với tất cả mọi người sử dụng khung thực thi: tất cả bạn đều quên tắt trình thực thi. Vì vậy, hãy thay đổi nó thành như sau:

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

Nếu bạn không, chương trình của bạn sẽ giữ một luồng không phải daemon đang hoạt động, đảm bảo chương trình của bạn sẽ không bao giờ thoát cho đến khi bạn gọi System.exit


5

Đối với những người không thể sử dụng phương pháp Java 8 mới waitFor(long timeout, TimeUnit unit)(vì họ đang sử dụng Android hoặc đơn giản là không thể nâng cấp), bạn có thể chỉ cần trích xuất nó từ mã nguồn JDK và thêm nó vào đâu đó trong tệp utils của bạn:

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

Thay đổi duy nhất mà tôi đã thực hiện đối với thay đổi ban đầu từ mã nguồn JDK8 là việc bổ sung Processtham số để chúng ta có thể gọiexitValue phương thức từ quy trình.

Các exitValue phương pháp sẽ trực tiếp cố gắng trả lại hoặc ném mộtIllegalThreadStateException nếu quá trình này vẫn chưa chấm dứt. Trong trường hợp đó, chúng tôi đợi thời gian chờ nhận được và kết thúc.

Phương thức trả về một boolean, vì vậy nếu nó trả về false thì bạn biết rằng bạn cần phải hủy quá trình theo cách thủ công.

Cách này có vẻ đơn giản hơn bất kỳ cách nào được đăng ở trên (chắc chắn là cuộc gọi trực tiếp sẽ chờ đợi).


3

Một giải pháp nhẹ cho các ứng dụng nhỏ:

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

2

Triển khai với tư cách là người được ủy quyền và không thực hiện được cuộc gọi nếu quá ngưỡng của bạn để hoàn thành.


2

Hãy thử sử dụng Bộ hẹn giờ (hoặc Ngủ ()), trong một chuỗi riêng biệt hoặc trong hàng đợi sự kiện của bạn nếu bạn có sẵn.


2

Có nhiều cách khác nhau để thực hiện việc này, nhưng tôi sẽ cân nhắc sử dụng Executor-- nó chỉ giúp bạn đóng gói việc chuyển giá trị thoát hoặc ngoại lệ từ luồng trở lại trình gọi ban đầu.

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

Tôi nghĩ rằng bạn không thể hoàn thành điều kiện cuộc đua, theo đó thời gian chờ đợi hết, và sau đó quá trình kết thúc ngay trước khi bạn gọi tiêu diệt ().


2

Tôi cũng đã thử nghiệm việc thực hiện công nhân và hoạt động như một cái duyên. Trong quy trình xử lý io, tôi đã thêm các luồng để xử lý stde và stdo. Nếu luồng công nhân hết thời gian, tôi cũng thoát khỏi luồng 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();
            }

Ồ, tôi thấy câu trả lời khác của bạn bây giờ. Tôi nghĩ sẽ tốt hơn nếu đặt tất cả điều này vào một câu trả lời duy nhất.
localhost

2

Đầu tiên là một số thông tin cơ bản, tôi đã gặp phải sự cố hết thời gian chờ khi chạy lệnh vì chương trình mà tôi cố gắng thực thi sẽ không bao giờ in bất kỳ thông tin gỡ lỗi hoặc lỗi nào nếu có lỗi và sẽ chỉ tiếp tục thử lại nội bộ dẫn đến quá trình bị kẹt bởi vì không bao giờ có lỗi hoặc luồng đầu ra khi nó đang thử lại.

Vì vậy, sau process.exec()hoặcprocess.start() ,

Nó sẽ bị mắc kẹt mãi mãi ở dòng này,

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

Theo java 1.8 với public boolean waitFor(long timeout,TimeUnit unit) phương thức, nó phải hết thời gian "lý tưởng" sau thời gian chờ được chỉ định nhưng trong trường hợp của tôi vì một số lý do, nó không bao giờ hết thời gian có thể là do tôi đang chạy ứng dụng dưới dạng dịch vụ windows (tôi đã kiểm tra quyền của người dùng và mọi thứ trên tài khoản nhưng nó không giúp được gì).

Vì vậy, tôi đã cố gắng triển khai nó với logic bên dưới, nơi chúng tôi sẽ tiếp tục kiểm tra luồng đầu vào với input.ready() và một cờ thời gian chờ. Giải pháp đơn giản này hoạt động giống như một sự quyến rũ so với tất cả các giải pháp khác đã tồn tại.

Mã:

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

Tôi đã gặp vấn đề tương tự trong Groovy và câu trả lời này đã giải quyết được vấn đề của tôi. Tất cả đã mất rất nhiều thời gian nên tôi sẽ chia sẻ lớp trình xây dựng đăng nhập cuối cùng của mình: gist.github.com/meonlol/df1174d2c1d84e7fe8db8710cd7beff1 Hy vọng nó sẽ giúp được ai đó.
leondepeon

0

Bạn có thể khởi chạy một Luồng ở chế độ ngủ trong thời gian bạn muốn và sau khi ngủ thay đổi một boolean mà bạn lặp lại trong phương thức executeCommandLine của mình.

Một cái gì đó tương tự (không được thử nghiệm cũng như không được biên dịch, giải pháp này là một mẫu thử nghiệm, bạn nên cấu trúc lại nó nếu nó phù hợp với bạn cần):

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

Lý do đằng sau việc đặt boolean trong một mảng cuối cùng là gì? Có một số gotcha để tránh?
rndmcnlly

2
Luồng bắt đầu là một lớp bên trong và để truy cập một biến bên ngoài, chúng phải là biến cuối cùng. Vì boolean là một kiểu gốc chứ không phải một đối tượng, việc đặt nó trong một mảng sẽ tạo nên một đối tượng của nó. Trong Process prop, bạn có thể thấy rằng Process p cũng là cuối cùng vì lý do tương tự.
Valentin Jacquemin

0

và đây là 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;
        }


}

0

Apache Commons Exec có thể giúp bạn làm điều đó.

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

0

Nếu sử dụng Java 8, tôi sẽ sử dụng câu trả lời Aleksander Blomskøld iepwaitFor (1, TimeUnit.MINUTE)

khác nếu Java 6/7 và sử dụng Swing, thì bạn có thể sử dụng 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
   }

0

Tôi biết đây thực sự là bài viết cũ; tôi cần một số trợ giúp với một dự án tương tự vì vậy tôi nghĩ tôi có thể cung cấp một số mã của tôi mà tôi đã làm việc và những mã hoạt động.

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;

Hy vọng điều này sẽ giúp ích cho tương lai: D


không phải phương thức read () là một cuộc gọi chặn, tức là một cuộc gọi chờ cho đến khi có dữ liệu? - nếu đúng như vậy thì điều này chỉ đợi mãi mãi cho đến khi dữ liệu được ghi và thời gian chờ chỉ được đánh giá sau khi nhận dữ liệu.
bvdb
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.