Frage Wie rufe ich eine Blockierungsmethode mit einem Timeout in Java auf?


Gibt es eine übliche nette Möglichkeit, eine Blockierungsmethode mit einem Timeout in Java aufzurufen? Ich möchte tun können:

// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it

wenn das Sinn macht.

Vielen Dank.


76
2017-07-22 10:18


Ursprung


Antworten:


Sie könnten einen Executor verwenden:

ExecutorService executor = Executors.newCachedThreadPool();
Callable<Object> task = new Callable<Object>() {
   public Object call() {
      return something.blockingMethod();
   }
};
Future<Object> future = executor.submit(task);
try {
   Object result = future.get(5, TimeUnit.SECONDS); 
} catch (TimeoutException ex) {
   // handle the timeout
} catch (InterruptedException e) {
   // handle the interrupts
} catch (ExecutionException e) {
   // handle other exceptions
} finally {
   future.cancel(true); // may or may not desire this
}

Wenn die future.get kehrt nicht in 5 Sekunden zurück, es wirft ein TimeoutException. Das Timeout kann in Sekunden, Minuten, Millisekunden oder einer anderen Einheit als Konstante in konfiguriert werden TimeUnit.

Siehe die JavaDoc für mehr Details.


126
2017-07-22 10:24



Sie könnten den Anruf in eine umbrechen FutureTask und verwende die Timeout-Version von get ().

Sehen http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html


9
2017-07-22 10:22



Dazu gibt es auch eine AspectJ-Lösung Jcabi-Aspekte Bibliothek.

@Timeable(limit = 30, unit = TimeUnit.MINUTES)
public Soup cookSoup() {
  // Cook soup, but for no more than 30 minutes (throw and exception if it takes any longer
}

Es kann nicht prägnanter werden, aber Sie müssen sich natürlich auf AspectJ verlassen und es in Ihren Build-Lebenszyklus einführen.

Es gibt einen Artikel, der es weiter erklärt: Beschränken Sie die Ausführungszeit für Java-Methoden


3
2017-07-28 22:37



Siehe auch Guavas Zeitlimiter welches einen Executor hinter den Kulissen benutzt.


2
2018-04-08 05:59



Thread thread = new Thread(new Runnable() {
    public void run() {
        something.blockingMethod();
    }
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
    thread.stop();
}

Beachten Sie, dass stop veraltet ist. Bessere Alternative ist, ein flüchtiges boolesches Flag zu setzen. Innerhalb von blockingMethod () überprüfen Sie es und beenden Sie es wie folgt:

import org.junit.*;
import java.util.*;
import junit.framework.TestCase;

public class ThreadTest extends TestCase {
    static class Something implements Runnable {
        private volatile boolean stopRequested;
        private final int steps;
        private final long waitPerStep;

        public Something(int steps, long waitPerStep) {
            this.steps = steps;
            this.waitPerStep = waitPerStep;
        }

        @Override
        public void run() {
            blockingMethod();
        }

        public void blockingMethod() {
            try {
                for (int i = 0; i < steps && !stopRequested; i++) {
                    doALittleBit();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        public void doALittleBit() throws InterruptedException {
            Thread.sleep(waitPerStep);
        }

        public void setStopRequested(boolean stopRequested) {
            this.stopRequested = stopRequested;
        }
    }

    @Test
    public void test() throws InterruptedException {
        final Something somethingRunnable = new Something(5, 1000);
        Thread thread = new Thread(somethingRunnable);
        thread.start();
        thread.join(2000);
        if (thread.isAlive()) {
            somethingRunnable.setStopRequested(true);
            thread.join(2000);
            assertFalse(thread.isAlive());
        } else {
            fail("Exptected to be alive (5 * 1000 > 2000)");
        }
    }
}

1
2017-09-19 08:09



Versuche dies. Einfachere Lösung. Garantiert, dass wenn der Block nicht innerhalb der Frist ausgeführt wurde. Der Prozess wird beendet und löst eine Ausnahme aus.

public class TimeoutBlock {

 private final long timeoutMilliSeconds;
    private long timeoutInteval=100;

    public TimeoutBlock(long timeoutMilliSeconds){
        this.timeoutMilliSeconds=timeoutMilliSeconds;
    }

    public void addBlock(Runnable runnable) throws Throwable{
        long collectIntervals=0;
        Thread timeoutWorker=new Thread(runnable);
        timeoutWorker.start();
        do{ 
            if(collectIntervals>=this.timeoutMilliSeconds){
                timeoutWorker.stop();
                throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated.");
            }
            collectIntervals+=timeoutInteval;           
            Thread.sleep(timeoutInteval);

        }while(timeoutWorker.isAlive());
        System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds.");
    }

    /**
     * @return the timeoutInteval
     */
    public long getTimeoutInteval() {
        return timeoutInteval;
    }

    /**
     * @param timeoutInteval the timeoutInteval to set
     */
    public void setTimeoutInteval(long timeoutInteval) {
        this.timeoutInteval = timeoutInteval;
    }
}

Beispiel:

try {
        TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds
        Runnable block=new Runnable() {

            @Override
            public void run() {
                //TO DO write block of code 
            }
        };

        timeoutBlock.addBlock(block);// execute the runnable block 

    } catch (Throwable e) {
        //catch the exception here . Which is block didn't execute within the time limit
    }

1
2017-08-09 04:44



Annehmen blockingMethod schlafe nur für ein paar Millis:

public void blockingMethod(Object input) {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Meine Lösung ist zu verwenden wait() und synchronized so was:

public void blockingMethod(final Object input, long millis) {
    final Object lock = new Object();
    new Thread(new Runnable() {

        @Override
        public void run() {
            blockingMethod(input);
            synchronized (lock) {
                lock.notify();
            }
        }
    }).start();
    synchronized (lock) {
        try {
            // Wait for specific millis and release the lock.
            // If blockingMethod is done during waiting time, it will wake
            // me up and give me the lock, and I will finish directly.
            // Otherwise, when the waiting time is over and the
            // blockingMethod is still
            // running, I will reacquire the lock and finish.
            lock.wait(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

So kannst du ersetzen

something.blockingMethod(input) 

zu

something.blockingMethod(input, 2000)

Ich hoffe es hilft.


0
2018-04-22 07:26