O problema executável
Você já está familiarizado com a interface Runnable e a classe Thread que a implementa. Vamos relembrar como é essa interface:
public interface Runnable {
public abstract void run();
}
Observe que o tipo de retorno do método run é void . Mas e se precisarmos que nossa thread retorne algum resultado de seu trabalho na forma de um número, uma string ou algum outro objeto? Então, devemos encontrar uma solução alternativa. Algo assim:
public class Fibonacci implements Runnable {
private final int index;
private int result;
public Fibonacci(int index) {
this.index = index;
}
@Override
public void run() {
int first = 0;
int second = 1;
if (index == 1) {
result = first;
} else if (index == 2) {
result = second;
} else {
for (int i = 0; i < index - 2; i++) {
int temp = second;
second += first;
first = temp;
}
result = second;
}
}
public static void printByIndex(int index) throws InterruptedException {
Fibonacci fibonacci = new Fibonacci(index);
Thread thread = new Thread(fibonacci);
thread.start();
thread.join();
System.out.println("Fibonacci number " + index + ": " + fibonacci.result);
}
}
Vamos executar o seguinte método principal :
public static void main(String[] args) throws Exception {
Fibonacci.printByIndex(10);
}
O console exibirá:
Este código tem várias desvantagens. Por exemplo, como resultado da chamada ao método join , o thread principal será bloqueado enquanto o método printByIndex for executado.
Interface chamável
Agora vamos ver a interface que o Java nos fornece imediatamente, que pode ser usada como uma alternativa ao Runnable . Esta é a interface Callable :
public interface Callable<V> {
V call() throws Exception;
}
Como você pode ver, assim como o Runnable , ele possui apenas um método. Este método tem o mesmo propósito do método run — ele contém o código que será executado em uma thread paralela. Quanto às diferenças, dê uma olhada no valor de retorno. Agora pode ser qualquer tipo que você especificar ao implementar a interface:
public class CurrentDate implements Callable<Long> {
@Override
public Long call() {
return new Date().getTime();
}
}
Outro exemplo:
Callable<String> task = () -> {
Thread.sleep(100);
return "Done";
};
Aqui está outra coisa útil — o método de chamada pode lançar um Exception . Isso significa que, ao contrário do método run , no método call não precisamos lidar com as exceções verificadas que ocorrem dentro do método:
|
|
interface futura
Outra interface que funciona de perto com Callable é Future . Future representa o resultado de cálculos assíncronos (paralelos) (o valor retornado pelo método de chamada ). Ele permite que você verifique se os cálculos foram feitos, aguarde a conclusão dos cálculos, obtenha o resultado dos cálculos e muito mais.
Métodos da interface do Futuro
-
boolean isDone() — este método retorna verdadeiro se esta tarefa (computação) for concluída. As tarefas que terminaram normalmente, terminaram com uma exceção ou foram canceladas são consideradas concluídas.
-
V get() — se necessário, esse método bloqueia o thread que o chamou e retorna o resultado dos cálculos quando eles são concluídos.
-
V get(long timeout, TimeUnit unit) — como o método anterior, este método bloqueia a thread que o chamou, esperando pelo resultado, mas apenas pelo tempo especificado pelos parâmetros do método.
-
boolean cancel(boolean mayInterruptIfRunning) — este método tenta interromper a execução da tarefa. Se a tarefa ainda não começou a ser executada, ela nunca será executada. Se a tarefa estava em andamento, o parâmetro mayInterruptIfRunning determina se será feita uma tentativa de interromper o thread que está executando a tarefa. Depois que o método cancel é chamado, o método isDone sempre retornará true .
-
boolean isCancelled() — este método retorna true se a tarefa for cancelada antes de terminar normalmente. O método sempre retornará true se o método cancel foi chamado anteriormente e retornou true .
Exemplo de código usando Callable e Future
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
public class Fibonacci implements Callable<Integer> {
private final int index;
public Fibonacci(int index) {
this.index = index;
}
@Override
public Integer call() {
int first = 0;
int second = 1;
if (index == 1) {
return first;
} else if (index == 2) {
return second;
} else {
for (int i = 0; i < index - 2; i++) {
int temp = second;
second += first;
first = temp;
}
return second;
}
}
public static Future<Integer> calculateAsync(int index) throws Exception {
Fibonacci fibonacci = new Fibonacci(index);
// The future object will represent the result of running the fibonacci task.
FutureTask<Integer> future = new FutureTask<>(fibonacci);
// Because the FutureTask class implements both the Future interface and the Runnable interface,
// you can pass instances of it to the Thread constructor
Thread thread = new Thread(future);
thread.start();
return future;
}
}
Vamos executar o seguinte método principal :
public static void main(String[] args) throws Exception {
Map<Integer, Future<Integer>> tasks = new HashMap<>();
for (int i = 10; i < 20; i++) {
tasks.put(i, Fibonacci.calculateAsync(i));
}
for (Map.Entry<Integer, Future<Integer>> entry : tasks.entrySet()) {
Future<Integer> task = entry.getValue();
int index = entry.getKey();
int result;
// Check whether the task is done
if (task.isDone()) {
// Get the result of the calculations
result = task.get();
} else {
try {
// Wait another 100 milliseconds for the result of the calculations
result = task.get(100, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
// Interrupt the task
task.cancel(true);
System.out.println("Fibonacci number " + index + " could not be calculated in the allotted time.");
return;
}
}
System.out.println("Fibonacci number " + index + ": " + result);
}
}
O console exibirá:
Número Fibonacci 17: 987
Número Fibonacci 18: 1597
Número Fibonacci 19: 2584 Número
Fibonacci 10: 34 Número
Fibonacci 11: 55 Número Fibonacci
12: 89 Número
Fibonacci 13: 144
Número Fibonacci 14: 233
Número Fibonacci 15: 377
GO TO FULL VERSION