¿Existe una buena forma estándar de llamar a un método de bloqueo con un tiempo de espera en Java? Quiero poder hacer:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
Si eso tiene sentido.
Gracias.
¿Existe una buena forma estándar de llamar a un método de bloqueo con un tiempo de espera en Java? Quiero poder hacer:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
Si eso tiene sentido.
Gracias.
Respuestas:
Podrías usar un Ejecutor:
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
}
Si future.get
no regresa en 5 segundos, lanza un TimeoutException
. El tiempo de espera se puede configurar en segundos, minutos, milisegundos o cualquier unidad disponible como constante enTimeUnit
.
Consulte JavaDoc para obtener más detalles.
BlockingMethodCallable
cuyo constructor acepte los parámetros que desea pasar blockingMethod()
y almacénelos como variables miembro (probablemente como finales). Luego, en el interior, call()
pase esos parámetros al archivo blockMethod()
.
future.cancel(true)
- El método cancel (boolean) en el tipo Future <Object> no es aplicable para los argumentos ()
Puede envolver la llamada en a FutureTask
y usar la versión de tiempo de espera de get ().
Ver http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html
Vea también TimeLimiter de Guava, que usa un Ejecutor detrás de escena.
También hay una solución AspectJ para eso con la biblioteca jcabi-examples .
@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
}
No puede ser más sucinto, pero debes depender de AspectJ e introducirlo en tu ciclo de vida de compilación, por supuesto.
Hay un artículo que lo explica con más detalle : Limitar el tiempo de ejecución del método Java
Es realmente genial que la gente intente implementar esto de tantas maneras. Pero la verdad es que NO hay forma.
La mayoría de los desarrolladores intentarían poner la llamada de bloqueo en un hilo diferente y tener un futuro o algún temporizador. PERO no hay forma en Java de detener un hilo externamente, y mucho menos en algunos casos muy específicos como los métodos Thread.sleep () y Lock.lockInterruptibly () que manejan explícitamente la interrupción del hilo.
Entonces, realmente tienes solo 3 opciones genéricas:
Coloque su llamada de bloqueo en un nuevo hilo y si el tiempo expira, simplemente continúe, dejando ese hilo colgando. En ese caso, debe asegurarse de que el hilo esté configurado para ser un hilo Daemon. De esta manera, el hilo no evitará que su aplicación finalice.
Utilice API de Java sin bloqueo. Entonces, para la red, por ejemplo, use NIO2 y use los métodos sin bloqueo. Para leer desde la consola, use Scanner.hasNext () antes de bloquear, etc.
Si su llamada de bloqueo no es una IO, sino su lógica, entonces puede verificar repetidamente Thread.isInterrupted()
si fue interrumpida externamente y tener otra llamada de hilo thread.interrupt()
en el hilo de bloqueo
Este curso sobre concurrencia https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY
realmente recorre esos fundamentos si realmente desea comprender cómo funciona en Java. En realidad, habla sobre esas limitaciones y escenarios específicos, y cómo abordarlos en una de las conferencias.
Personalmente, trato de programar sin usar el bloqueo de llamadas tanto como sea posible. Existen kits de herramientas como Vert.x, por ejemplo, que hacen que sea realmente fácil y eficaz realizar operaciones de IO y no IO de forma asíncrona y sin bloqueo.
Espero que ayude
Thread thread = new Thread(new Runnable() {
public void run() {
something.blockingMethod();
}
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
thread.stop();
}
Tenga en cuenta que la parada está en desuso, la mejor alternativa es establecer una bandera booleana volátil, dentro deckingMethod () verifique y salga, así:
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)");
}
}
}
Prueba esto. Solución más sencilla. Garantiza que si el bloqueo no se ejecuta dentro del límite de tiempo. el proceso terminará y lanzará una excepción.
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;
}
}
ejemplo:
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
}
Te estoy dando aquí el código completo. En lugar del método al que estoy llamando, puedes usar tu método:
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
}
}
}
Suponga que blockingMethod
solo duerme por algunos milis:
public void blockingMethod(Object input) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Mi solución es usar wait()
y synchronized
así:
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();
}
}
}
Entonces puedes reemplazar
something.blockingMethod(input)
a
something.blockingMethod(input, 2000)
Espero eso ayude.
Necesita una implementación de disyuntor como la presente en el proyecto a prueba de fallas en GitHub.