Dúvida com Future e Callable

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:

[code]ExecutorService executor = Executors.newCachedThreadPool();
Future future = executor.submit(new ClasseQueImplementaCallable());

try {
future.get(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
future.cancel(true);
}
//…[/code]
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

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.

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:

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);
    }
}

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);
    }
}

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!

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”.

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

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();
}