Existe uma maneira legal padrão de chamar um método de bloqueio com um tempo limite em Java? Eu quero ser capaz de fazer:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
se isso faz sentido.
Obrigado.
Existe uma maneira legal padrão de chamar um método de bloqueio com um tempo limite em Java? Eu quero ser capaz de fazer:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
se isso faz sentido.
Obrigado.
Respostas:
Você poderia usar um 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.get
não retornar em 5 segundos, ele gerará um TimeoutException
. O tempo limite pode ser configurado em segundos, minutos, milissegundos ou qualquer unidade disponível como uma constante em TimeUnit
.
Veja o JavaDoc para mais detalhes.
BlockingMethodCallable
cujo construtor aceita os parâmetros que você deseja passar blockingMethod()
e armazene-os como variáveis de membro (provavelmente como finais). Em seguida, call()
passe esses parâmetros para o blockMethod()
.
future.cancel(true)
- O método cancel (boolean) no tipo Future <Object> não é aplicável para os argumentos ()
Você poderia envolver a chamada em a FutureTask
e usar a versão de tempo limite de get ().
Consulte http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html
Veja também TimeLimiter da Guava, que usa um Executor nos bastidores.
Também existe uma solução AspectJ para isso com a biblioteca de aspectos jcabi .
@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
}
Não pode ser mais sucinto, mas você tem que depender do AspectJ e introduzi-lo em seu ciclo de vida de construção, é claro.
Há um artigo que explica isso melhor: Limite o tempo de execução do método Java
É muito bom que as pessoas tentem implementar isso de tantas maneiras. Mas a verdade é que NÃO há como.
A maioria dos desenvolvedores tentaria colocar a chamada de bloqueio em um thread diferente e teria um futuro ou algum temporizador. MAS não há como em Java parar um thread externamente, muito menos alguns casos muito específicos como os métodos Thread.sleep () e Lock.lockInterruptibly () que tratam explicitamente da interrupção do thread.
Então, na verdade, você tem apenas 3 opções genéricas:
Coloque sua chamada de bloqueio em um novo tópico e, se o tempo expirar, siga em frente, deixando o tópico suspenso. Nesse caso, você deve certificar-se de que o thread está definido como um thread do Daemon. Dessa forma, o thread não interromperá o encerramento do seu aplicativo.
Use APIs Java sem bloqueio. Portanto, para rede, por exemplo, use NIO2 e use os métodos sem bloqueio. Para ler a partir do console, use Scanner.hasNext () antes de bloquear etc.
Se sua chamada de bloqueio não for um IO, mas sua lógica, você pode verificar repetidamente para Thread.isInterrupted()
verificar se foi interrompido externamente e ter outra chamada de thread thread.interrupt()
no thread de bloqueio
Este curso sobre simultaneidade https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY
realmente percorre esses fundamentos se você realmente deseja entender como funciona em Java. Na verdade, fala sobre essas limitações e cenários específicos e como abordá-los em uma das palestras.
Eu pessoalmente tento programar sem usar o máximo possível de chamadas de bloqueio. Existem kits de ferramentas como o Vert.x, por exemplo, que tornam realmente fácil e eficiente fazer IO e nenhuma operação IO de forma assíncrona e sem bloqueios.
Espero que ajude
Thread thread = new Thread(new Runnable() {
public void run() {
something.blockingMethod();
}
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
thread.stop();
}
Observe que a parada está obsoleta, a melhor alternativa é definir algum sinalizador booleano volátil, dentro de BlockMethod (), verifique e saia, assim:
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)");
}
}
}
Experimente isso. Solução mais simples. Garante que se o bloco não for executado dentro do limite de tempo. o processo será encerrado e lançará uma exceção.
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;
}
}
exemplo:
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
}
Estou dando aqui o código completo. No lugar do método que estou chamando, você pode usar seu 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
}
}
}
Suponha que blockingMethod
apenas durma por alguns milis:
public void blockingMethod(Object input) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Minha solução é usar wait()
e synchronized
assim:
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();
}
}
}
Então você pode substituir
something.blockingMethod(input)
para
something.blockingMethod(input, 2000)
Espero que ajude.
Você precisa de uma implementação de disjuntor como a presente no projeto à prova de falhas no GitHub.