web-dev-qa-db-fra.com

java délai d'expiration du processus natif

En ce moment, j'exécute un processus natif en utilisant ce qui suit:

Java.lang.Process process = Runtime.getRuntime().exec(command); 
int returnCode = process.waitFor();

Supposons qu'au lieu d'attendre le retour du programme, je souhaite terminer si un certain laps de temps s'est écoulé. Comment puis-je faire cela?

43
deltanovember

Voici comment le Plexus CommandlineUtils le fait:

Process p;

p = cl.execute();

...

if ( timeoutInSeconds <= 0 )
{
    returnValue = p.waitFor();
}
else
{
    long now = System.currentTimeMillis();
    long timeoutInMillis = 1000L * timeoutInSeconds;
    long finish = now + timeoutInMillis;
    while ( isAlive( p ) && ( System.currentTimeMillis() < finish ) )
    {
        Thread.sleep( 10 );
    }
    if ( isAlive( p ) )
    {
        throw new InterruptedException( "Process timeout out after " + timeoutInSeconds + " seconds" );
    }
    returnValue = p.exitValue();
}

public static boolean isAlive( Process p ) {
    try
    {
        p.exitValue();
        return false;
    } catch (IllegalThreadStateException e) {
        return true;
    }
}
19
Rich Seller

Toutes les autres réponses sont correctes mais elles peuvent être rendues plus robustes et efficaces en utilisant FutureTask.

Par exemple,

private static final ExecutorService THREAD_POOL 
    = Executors.newCachedThreadPool();

private static <T> T timedCall(Callable<T> c, long timeout, TimeUnit timeUnit)
    throws InterruptedException, ExecutionException, TimeoutException
{
    FutureTask<T> task = new FutureTask<T>(c);
    THREAD_POOL.execute(task);
    return task.get(timeout, timeUnit);
}

try {
    int returnCode = timedCall(new Callable<Integer>() {
        public Integer call() throws Exception {
            Java.lang.Process process = Runtime.getRuntime().exec(command); 
            return process.waitFor();
        }
    }, timeout, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    // Handle timeout here
}

Si vous effectuez cette opération à plusieurs reprises, le pool de threads est plus efficace car il met en cache les threads.

51
ZZ Coder

Si vous utilisez Java 8, vous pouvez simplement utiliser le nouveau waitFor avec timeout :

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

Qu'en est-il de la manière Groovy

public void yourMethod() {
    ...
    Process process = new ProcessBuilder(...).start(); 
    //wait 5 secs or kill the process
    waitForOrKill(process, TimeUnit.SECONDS.toMillis(5));
    ...
}

public static void waitForOrKill(Process self, long numberOfMillis) {
    ProcessRunner runnable = new ProcessRunner(self);
    Thread thread = new Thread(runnable);
    thread.start();
    runnable.waitForOrKill(numberOfMillis);
}

protected static class ProcessRunner implements Runnable {
    Process process;
    private boolean finished;

    public ProcessRunner(Process process) {
        this.process = process;
    }

    public void run() {
        try {
            process.waitFor();
        } catch (InterruptedException e) {
            // Ignore
        }
        synchronized (this) {
            notifyAll();
            finished = true;
        }
    }

    public synchronized void waitForOrKill(long millis) {
        if (!finished) {
            try {
                wait(millis);
            } catch (InterruptedException e) {
                // Ignore
            }
            if (!finished) {
                process.destroy();
            }
        }
    }
}
6
mickthompson

juste modifié un peu selon mes besoins. le temps mort est de 10 secondes ici. le processus est détruit après 10 secondes s'il ne se termine pas.

public static void main(String arg[]) {

    try {
        Process p = Runtime.getRuntime().exec("\"C:/Program Files/VanDyke Software/SecureCRT/SecureCRT.exe\"");
        long now = System.currentTimeMillis(); 
        long timeoutInMillis = 1000L * 10; 
        long finish = now + timeoutInMillis; 
        while ( isAlive( p ) ) { 
            Thread.sleep( 10 ); 
            if ( System.currentTimeMillis() > finish ) {
                p.destroy();
            }
        }
    } catch (Exception err) {
        err.printStackTrace();
    }
}

public static boolean isAlive( Process p ) {  
    try {  
        p.exitValue();  
        return false;  
    } catch (IllegalThreadStateException e) {  
        return true;  
    }  
}  
4
nuwan

Vous auriez besoin d'un thread 2. qui interrompt le thread qui appelle .waitFor (); Une synchronisation non triviale sera nécessaire pour le rendre robuste, mais les bases sont les suivantes:

TimeoutThread:

 Thread.sleep(timeout);
 processThread.interrupt();

ProcessThread:

  try {
      proc.waitFor(); 
    } catch (InterruptedException e) {
       proc.destroy();
    }
2
nos