Dúvida com Future e Callable

8 respostas
V
Olá, Na minha aplicação tenho que fazer um processamento assíncrono e caso este processamento nao termine em um tempo X, 10 segundos por exemplo, preciso finaliza-lo. De modo geral criei um método que tem o código abaixo:
ExecutorService executor = Executors.newCachedThreadPool();
  Future<Boolean> future = executor.submit(new ClasseQueImplementaCallable());

  try {
    future.get(10, TimeUnit.SECONDS);
  } catch (TimeoutException e) {
      future.cancel(true);
 }
//...
Debugando a aplicação percebi que a aplicação fica parada durante a execução da thread. Por ex:
System.out.println("inicio") ;
metodoConcorrente();
System.out.println("fim") ;
O que eu quero é que o metodoConcorrente() acima seja executado em background (com o timeout), e com isto as Strings "inicio" e "fim" sejam impressas rapidamente. Eu teria que colocar o metodoConcorrente() em umaThread ou algo do tipo?

Agradeço a ajuda,
[ ]'s

8 Respostas

E

Use “get” apenas se quiser esperar a execução do seu Callable. Pelo que vi, você não precisa do valor de retorno do seu Callable (que é um Boolean, certo)?

Bom, como você precisa invocar um InterruptedException após 10 segundos, você pode criar um timer de 10 segundos, e pôr no método a ser invocado pelo timer o “future.cancel”. E depois da conclusão da sua tarefa que você quer rodar em paralelo, você põe o get ou então o cancelamento do timer.

V

Realmente não preciso do retorno (que é um Boolean). Coloquei o get apenas pelo time-out que ele disponibiliza.

Você teria algum exemplo de um timer como este que vc citou? Seria de grande ajuda :slight_smile:

T

Quando vc chama “future.get” a execução irá parar naquela linha. Se o teu método “metodoConcorrente” chamar “future.get”, ele irá mesmo parar.
Como testar…

public class TestMe {
    private static boolean executed = false;
    private static boolean timeout = false;

    private static void executeBackground() throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<Boolean> future = executor.submit(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                Thread.sleep(TimeUnit.SECONDS.toMillis(3));
                executed = true;
                return Boolean.TRUE;
            }
        });

        try {
            future.get(1, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            timeout = true;
        }
    }

    @Test
    public void test() throws ParseException, ExecutionException, InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    executeBackground();
                } catch (Exception e) {
                    Assert.fail("Exception!!!!");
                }
            }
        }) ;
        thread.start();
        Assert.assertFalse(timeout);
        Assert.assertFalse(executed);

        Thread.sleep(TimeUnit.SECONDS.toMillis(10));

        Assert.assertTrue(timeout);
        Assert.assertFalse(executed);
    }
}
T

Oopsss… um jeito melhor de testar.

public class TestMe {
    private static boolean executed = false;
    private static boolean timeout = false;

    private Future<Boolean> executeBackground() throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<Boolean> future = executor.submit(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                Thread.sleep(TimeUnit.SECONDS.toMillis(3));
                executed = true;
                return Boolean.TRUE;
            }
        });
        return future;
    }

    @Test
    public void test() throws ParseException, ExecutionException, InterruptedException {
        Future<Boolean> future = executeBackground();
        Assert.assertFalse(timeout);
        Assert.assertFalse(executed);

        try {
            future.get(1, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            timeout = true;
        }

        Assert.assertTrue(timeout);
        Assert.assertFalse(executed);
    }
}
V

Entendi,
então no meu caso seria necessário apenas colocar o metodoConcorrente() dentro de uma thread, assim como vc fez no primeiro teste.
Aí eu teria um outro método, o executaMetodoConcorrente(), por exemplo, que cria esta thread e a executa.

Muito obrigado pela ajuda!

T

Na verdade vc não precisa criar outra thread. Quando vc chama “executor.submit”, o executor já cria uma thread separada. O truque é retornar o objeto “future” no teu “executeBackground”. Quando vc realmente precisar do valor retornado por “executeBackground”, vc chama “future.get”.

V

Ah sim, entendi. Seria igual vc fez no segundo teste. Faz mais sentido mesmo.
E d novo valeu pela ajuda :slight_smile:

T

Nota: O uso do “future” é bom quando vc não se importa com o resultado da thread criada por “executor.submit”, ou quando vc tem alguma coisa como…

public Double calculaContaComplicada() {
  Future<Double> complicadoA = executaCalculosMatematicosComplicadosA();
  Future<Double> complicadoB = executaCalculosMatematicosComplicadosA();
  return complicadoA.get() + complicadoB.get();
}
Criado 18 de julho de 2012
Ultima resposta 18 de jul. de 2012
Respostas 8
Participantes 3