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.
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.
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, wyrzuca TimeoutException
. Limit czasu można skonfigurować w sekundach, minutach, milisekundach lub w dowolnej jednostce dostępnej jako stała w TimeUnit
.
Więcej szczegółów znajdziesz w JavaDoc .
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ątrz call()
przekaż te parametry do blockMethod()
.
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
Zobacz także TimeLimiter Guavy, który używa Executora za kulisami.
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ątku thread.interrupt()
w wątku blokującym
Ten 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
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
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)");
}
}
}
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
}
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
}
}
}
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()
i synchronized
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.
Potrzebujesz implementacji wyłącznika, takiej jak ta obecna w bezpiecznym projekcie na GitHub.