Czy istnieje standardowy sposób na wywołanie 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.
java
concurrency
jjujuma
źródło
źródło
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 wróci w ciągu 5 sekund, wyrzucaTimeoutException
. Limit czasu można skonfigurować w sekundach, minutach, milisekundach lub w dowolnej jednostce dostępnej jako stała wTimeUnit
.Więcej szczegółów znajdziesz w JavaDoc .
źródło
BlockingMethodCallable
której konstruktor akceptuje parametry, do których chcesz przekazaćblockingMethod()
i przechowuje je jako zmienne składowe (prawdopodobnie jako ostateczne). Następnie wewnątrzcall()
przekaż te parametry doblockMethod()
.future.cancel(true)
- Metoda cancel (boolean) w typie Future <Object> nie ma zastosowania dla arguments ()Możesz zawinąć wywołanie w a
FutureTask
i użyć wersji get () z limitem czasu.Zobacz http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html
źródło
Zobacz także TimeLimiter Guavy, który używa Executora za kulisami.
źródło
To naprawdę wspaniałe, że ludzie próbują to wdrożyć na tak wiele sposobów. Ale prawda jest taka, że NIE MA sposobu.
Większość programistów próbowałaby umieścić wywołanie blokujące w innym wątku i mieć czas na przyszłość lub jakiś czas. ALE nie ma sposobu w Javie, aby zatrzymać wątek zewnętrznie, nie mówiąc już o kilku bardzo specyficznych przypadkach, takich jak metody Thread.sleep () i Lock.lockInterruptible (), które jawnie obsługują przerwanie wątku.
Więc naprawdę masz tylko 3 ogólne opcje:
Umieść połączenie blokujące w nowym wątku, a jeśli upłynie czas, po prostu przejdź dalej, pozostawiając ten wątek zawieszony. W takim przypadku powinieneś upewnić się, że wątek jest ustawiony jako wątek Daemon. W ten sposób wątek nie zatrzyma zakończenia aplikacji.
Używaj nieblokujących interfejsów API Java. Na przykład w przypadku sieci użyj NIO2 i użyj metod nieblokujących. Do czytania z konsoli użyj Scanner.hasNext () przed zablokowaniem itp.
Jeśli wywołanie blokujące nie jest we / wy, ale logiką, możesz wielokrotnie
Thread.isInterrupted()
sprawdzać, czy zostało przerwane zewnętrznie, i mieć kolejne wywołanie wątkuthread.interrupt()
w wątku blokującymTen kurs dotyczący współbieżności https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY
naprawdę omawia te podstawy, jeśli naprawdę chcesz zrozumieć, jak to działa w Javie. W rzeczywistości mówi o tych konkretnych ograniczeniach i scenariuszach oraz o tym, jak się do nich zabrać na jednym z wykładów.
Osobiście staram się programować bez blokowania połączeń tak często, jak to możliwe. Istnieją na przykład zestawy narzędzi, takie jak Vert.x, które sprawiają, że wykonywanie operacji we / wy jest naprawdę łatwe i wydajne, a operacje we / wy nie są wykonywane w sposób asynchroniczny i nieblokujący.
Mam nadzieję, że to pomoże
źródło
Istnieje również rozwiązanie AspectJ z biblioteką jcabi-Aspects .
@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łe, ale oczywiście musisz polegać na AspectJ i wprowadzić go w cykl życia kompilacji.
Jest artykuł wyjaśniający to dokładniej: Ograniczanie czasu wykonania metody Java
źródło
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 jakiejś niestabilnej flagi boolowskiej, wewnątrz blockingMethod () sprawdź to i zakończ, na przykład:
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)"); } } }
źródło
Spróbuj tego. Prostsze rozwiązanie. Gwarantuje, że jeśli blok nie zostanie wykonany w terminie. proces zakończy się i zgłosi 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 }
źródło
Podaję Ci pełny kod. Zamiast metody, którą wywołuję, możesz użyć swojej metody:
public class NewTimeout { public String simpleMethod() { return "simple method"; } public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadScheduledExecutor(); Callable<Object> task = new Callable<Object>() { public Object call() throws InterruptedException { Thread.sleep(1100); return new NewTimeout().simpleMethod(); } }; Future<Object> future = executor.submit(task); try { Object result = future.get(1, TimeUnit.SECONDS); System.out.println(result); } catch (TimeoutException ex) { System.out.println("Timeout............Timeout..........."); } catch (InterruptedException e) { // handle the interrupts } catch (ExecutionException e) { // handle other exceptions } finally { executor.shutdown(); // may or may not desire this } } }
źródło
Załóżmy,
blockingMethod
że śpię przez kilka milisekund:public void blockingMethod(Object input) { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }
Moje rozwiązanie polega na użyciu
wait()
isynchronized
tak: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 wymienić
something.blockingMethod(input)
do
something.blockingMethod(input, 2000)
Mam nadzieję, że to pomoże.
źródło
Potrzebujesz implementacji wyłącznika, takiej jak ta obecna w bezpiecznym projekcie na GitHub.
źródło