Pytanie Jak wywołać metodę blokowania z limitem czasu w Javie?


Czy istnieje standardowy sposób wywoływania metody blokowania z limitem czasu w Javie? Chcę móc:

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

Jeśli to ma sens.

Dzięki.


76
2017-07-22 10:18


pochodzenie


Jako referencję, sprawdź Współbieżność Java w praktyce Briana Goetza, s. 126 - 134, w szczególności rozdział 6.3.7 "Umieszczanie limitów czasowych na zadaniach" - brown.2179


Odpowiedzi:


Możesz użyć Executora:

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
}

Jeśli future.get nie wraca w 5 sekund, rzuca a TimeoutException. Limit czasu można skonfigurować w sekundach, minutach, milisekundach lub dowolnej jednostce dostępnej jako stała TimeUnit.

Zobacz JavaDoc po więcej szczegółów.


126
2017-07-22 10:24



Metoda blokowania będzie nadal działać nawet po przekroczeniu limitu czasu, prawda? - Ivan Dubrov
To zależy od przyszłości. W zależności od tego, co robi metoda blokująca, może się ona zakończyć, ale nie musi. - skaffman
przetestowany i pracował tutaj. - Jus12
jak mogę przekazać parametr do metody blockingMethod ()? Dzięki! - Robert A Henru
@RobertAHenru: Utwórz nową klasę o nazwie BlockingMethodCallable którego contructor akceptuje parametry, które chcesz przekazać blockingMethod() i przechowuj je jako zmienne członkowskie (prawdopodobnie jako ostateczne). Następnie wewnątrz call() przekazać te parametry do blockMethod(). - Vite Falcon


Możesz zawinąć wywołanie w FutureTask i użyj timeoutowej wersji get ().

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


9
2017-07-22 10:22



FutureTask nie jest sam w sobie asynchroniczny, prawda? Sam w sobie po prostu robi rzeczy synchronicznie, musisz połączyć je z Executorem, aby uzyskać egynchroniczne zachowanie. - skaffman
Tak, potrzebujesz executora takiego jak to, co zakodowałeś - Colin Goudie


Istnieje również rozwiązanie AspectJ do tego jcabi-aspekty biblioteka.

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

Nie może być bardziej zwięzły, ale musisz oczywiście polegać na AspectJ i przedstawić go w cyklu życia kompilacji.

Istnieje artykuł wyjaśniający to dalej: Limit czasu wykonania metody Java


3
2017-07-28 22:37





Zobacz także Guava TimeLimiter który używa Executora za kulisami.


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

Zauważ, że stop jest przestarzały, lepszą alternatywą jest ustawienie zmiennej flagi binarnej, wewnątrz metody blockingMethod () sprawdź i zakończ, tak jak poniżej:

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





Spróbuj tego. Bardziej proste rozwiązanie. Gwarantuje, że jeśli blok nie zostanie wykonany w terminie. proces zakończy się i zgłasza wyjątek.

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

przykład:

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





Założyć blockingMethod po prostu spać za kilka millis:

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

Moim rozwiązaniem jest użycie wait() i synchronized lubię to:

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

Więc możesz zastąpić

something.blockingMethod(input) 

do

something.blockingMethod(input, 2000)

Mam nadzieję, że to pomoże.


0
2018-04-22 07:26





Potrzebujesz wyłącznik obwodu wdrożenie takie jak obecne w bezpieczny w razie awarii projekt na GitHub.


0
2017-09-09 15:19