Uruchamiany problem
Interfejs Runnable i implementująca go klasa Thread są już Ci znane . Przypomnijmy sobie, jak wygląda ten interfejs:
public interface Runnable {
public abstract void run();
}
Należy zauważyć, że wartość zwracana przez metodę run to void . Ale co, jeśli potrzebujemy uzyskać jakiś wynik wątku w postaci liczby, ciągu znaków lub dowolnego innego obiektu? Następnie musisz jakoś się wydostać, na przykład w ten sposób:
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);
}
}
Uruchommy tę główną metodę :
public static void main(String[] args) throws Exception {
Fibonacci.printByIndex(10);
}
Następujące dane zostaną wyprowadzone na konsolę:
Ten kod ma kilka wad. Na przykład główny wątek zostanie zablokowany podczas wywoływania metody printByIndex , ponieważ zawiera ona wywołanie metody join .
Wywoływalny interfejs
Teraz spójrzmy na interfejs, który Java zapewnia nam od razu po wyjęciu z pudełka i który może być alternatywą dla Runnable . To jest interfejs Callable :
public interface Callable<V> {
V call() throws Exception;
}
Jak widać, podobnie jak Runnable , ma tylko jedną metodę. Przeznaczenie metody jest takie samo jak metody run – będzie zawierała kod, który będzie wykonywany w wątku równoległym. Z różnic można zauważyć wartość zwracaną. Teraz może to być dowolny typ, który określisz podczas implementacji interfejsu:
public class CurrentDate implements Callable<Long> {
@Override
public Long call() {
return new Date().getTime();
}
}
Inny przykład:
Callable<String> task = () -> {
Thread.sleep(100);
return "Done";
};
Kolejna przydatna rzecz: metoda call może zgłosić wyjątek , więc w przeciwieństwie do metody run nie musisz śledzić sprawdzanych wyjątków, które występują wewnątrz metody:
|
|
Przyszły interfejs
Kolejnym interfejsem, który będzie ściśle współpracował z Callable, jest Future . Future reprezentuje wynik obliczeń asynchronicznych (równoległych) (ta sama wartość zwracana przez metodę call ). Pozwala sprawdzić, czy obliczenia zostały zakończone, poczekać na zakończenie obliczeń, uzyskać wynik obliczeń i nie tylko.
Metody
-
boolean isDone() - metoda zwraca true , jeśli to zadanie (obliczenie) zostało zakończone. Zadania, które zakończyły się normalnie, zakończyły się z wyjątkiem lub zostały anulowane, są uważane za zakończone.
-
V get() - metoda w razie potrzeby blokuje wątek, który ją wywołał, a na koniec obliczeń zwraca ich wynik.
-
V get(long timeout, TimeUnit unit) - podobnie jak poprzednia metoda, blokuje wątek, który ją wywołał, czekając na wynik, ale tylko na czas określony parametrami metody.
-
boolean cancel(boolean mayInterruptIfRunning) - metoda próbuje zatrzymać wykonywanie zadania. Jeśli zadanie nie zostało jeszcze uruchomione, nigdy nie zostanie uruchomione. Jeśli zadanie było w toku, parametr mayInterruptIfRunning określa, czy zostanie podjęta próba przerwania wątku uruchamiającego zadanie. Po wywołaniu metody anulowania metoda isDone zawsze zwróci wartość true .
-
boolean isCancelled() — metoda zwraca wartość true , jeśli zadanie zostało anulowane przed jego normalnym zakończeniem. Metoda zawsze zwróci wartość true , jeśli metoda anulowania została wcześniej wywołana i zwróciła wartość true .
Przykład kodu do wywołania i przyszłości
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;
}
}
Uruchommy tę główną metodę :
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);
}
}
Następujące dane zostaną wyprowadzone na konsolę:
17 Fibonacciego: 987
18 Fibonacciego: 1597 19 Fibonacciego: 2584 10
Fibonacciego
: 34
11 Fibonacciego: 55 12
Fibonacciego: 89
13 Fibonacciego 14: 144
Fibonacciego 14: 233
Fibonacciego 15: 377
GO TO FULL VERSION