C'è un bel modo standard per chiamare un metodo di blocco con un timeout in Java? Voglio essere in grado di fare:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
se questo ha un senso.
Grazie.
C'è un bel modo standard per chiamare un metodo di blocco con un timeout in Java? Voglio essere in grado di fare:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
se questo ha un senso.
Grazie.
Risposte:
Potresti usare un Executor:
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
}
Se future.getnon ritorna entro 5 secondi, lancia un file TimeoutException. Il timeout può essere configurato in secondi, minuti, millisecondi o qualsiasi unità disponibile come costante in TimeUnit.
Vedi il JavaDoc per maggiori dettagli.
BlockingMethodCallablecui costruttore accetta i parametri a cui vuoi passare blockingMethod()e memorizzali come variabili membro (probabilmente come finali). Quindi call()passare questi parametri al file blockMethod().
future.cancel(true)- Il metodo cancel (booleano) nel tipo Future <Object> non è applicabile per gli argomenti ()
È possibile racchiudere la chiamata in a FutureTaske utilizzare la versione timeout di get ().
Vedi http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html
Vedi anche TimeLimiter di Guava che usa un Executor dietro le quinte.
C'è anche una soluzione AspectJ per quello con la libreria 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
}
Non può essere più succinto, ma devi dipendere da AspectJ e introdurlo nel ciclo di vita della build, ovviamente.
C'è un articolo che lo spiega ulteriormente: Limita il tempo di esecuzione del metodo Java
È davvero fantastico che le persone provino a implementarlo in così tanti modi. Ma la verità è che NON c'è modo.
La maggior parte degli sviluppatori proverebbe a mettere la chiamata di blocco in un thread diverso e avere un futuro o un timer. MA non c'è modo in Java di fermare un thread esternamente, per non parlare di alcuni casi molto specifici come i metodi Thread.sleep () e Lock.lockInterruptibly () che gestiscono esplicitamente l'interruzione del thread.
Quindi in realtà hai solo 3 opzioni generiche:
Metti la tua chiamata di blocco su un nuovo thread e se il tempo scade vai avanti, lasciando quel thread in sospeso. In tal caso dovresti assicurarti che il thread sia impostato per essere un thread Daemon. In questo modo il thread non interromperà la chiusura dell'applicazione.
Utilizza API Java non bloccanti. Quindi, per la rete, ad esempio, usa NIO2 e usa i metodi non bloccanti. Per leggere dalla console usa Scanner.hasNext () prima di bloccare ecc.
Se la tua chiamata di blocco non è un IO, ma la tua logica, puoi controllare ripetutamente Thread.isInterrupted()se è stata interrotta esternamente e avere un'altra chiamata di thread thread.interrupt()sul thread di blocco
Questo corso sulla concorrenza https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY
cammina davvero attraverso questi fondamenti se vuoi davvero capire come funziona in Java. In realtà parla di quei limiti e scenari specifici e di come affrontarli in una delle lezioni.
Personalmente cerco di programmare senza utilizzare il più possibile il blocco delle chiamate. Ci sono toolkit come Vert.x, ad esempio, che rendono davvero facile ed efficiente eseguire operazioni di I / O e nessuna operazione di I / O in modo asincrono e in modo non bloccante.
spero possa essere d'aiuto
Thread thread = new Thread(new Runnable() {
public void run() {
something.blockingMethod();
}
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
thread.stop();
}
Nota che lo stop è deprecato, un'alternativa migliore è impostare un flag booleano volatile, all'interno di blockingMethod () selezionalo ed esci, in questo modo:
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)");
}
}
}
Prova questo. Soluzione più semplice. Garantisce che se il blocco non è stato eseguito entro il limite di tempo. il processo terminerà e genererà un'eccezione.
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;
}
}
esempio :
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
}
Ti sto dando qui il codice completo. Al posto del metodo che sto chiamando, puoi usare il tuo metodo:
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
}
}
}
Supponi di blockingMethoddormire solo per qualche millesimo:
public void blockingMethod(Object input) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
La mia soluzione è usare wait()e in synchronizedquesto modo:
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();
}
}
}
Quindi puoi sostituire
something.blockingMethod(input)
per
something.blockingMethod(input, 2000)
Spero che sia d'aiuto.
È necessaria un'implementazione dell'interruttore di circuito come quella presente nel progetto fail - safe su GitHub.