Kjørbar-problemet
Du er allerede kjent med Runnable- grensesnittet og Thread -klassen som implementerer det. La oss huske hvordan dette grensesnittet ser ut:
public interface Runnable {
public abstract void run();
}
Merk at kjøringsmetodens returtype er ugyldig . Men hva om vi trenger at tråden vår skal returnere et resultat av arbeidet i form av et tall, en streng eller et annet objekt? Da må vi komme med en løsning. Noe sånt som dette:
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);
}
}
La oss kjøre følgende hovedmetode :
public static void main(String[] args) throws Exception {
Fibonacci.printByIndex(10);
}
Konsollen vil vise:
Denne koden har flere ulemper. For eksempel, som et resultat av kallet til join- metoden, vil hovedtråden blokkere mens printByIndex -metoden utføres.
Anropbart grensesnitt
La oss nå se på grensesnittet som Java gir oss ut av boksen, som kan brukes som et alternativ til Runnable . Dette er det anropbare grensesnittet:
public interface Callable<V> {
V call() throws Exception;
}
Som du kan se, akkurat som Runnable , har den bare én metode. Denne metoden tjener samme formål som kjøremetoden - den inneholder koden som vil bli utført i en parallell tråd. Når det gjelder forskjeller, ta en titt på returverdien. Nå kan det være hvilken som helst type du angir når du implementerer grensesnittet:
public class CurrentDate implements Callable<Long> {
@Override
public Long call() {
return new Date().getTime();
}
}
Et annet eksempel:
Callable<String> task = () -> {
Thread.sleep(100);
return "Done";
};
Her er noe annet nyttig - anropsmetoden kan gi et unntak . Det betyr at, i motsetning til kjøringsmetoden , i kallemetoden trenger vi ikke å håndtere de sjekkede unntakene som forekommer inne i metoden:
|
|
Fremtidig grensesnitt
Et annet grensesnitt som jobber tett med Callable er Future . Fremtiden representerer resultatet av asynkrone (parallelle) beregninger (verdien returnert av anropsmetoden ) . Den lar deg sjekke om beregningene er utført, vente på at beregningene er ferdige, få resultatet av beregningene og mer.
Metoder for fremtidens grensesnitt
-
boolean isDone() — denne metoden returnerer true hvis denne oppgaven (beregningen) er utført. Oppgaver som ble avsluttet normalt, avsluttet med unntak eller ble kansellert anses som utført.
-
V get() — om nødvendig blokkerer denne metoden tråden som kalte den, og returnerer resultatet av beregningene når de er ferdige.
-
V get(lang tidsavbrudd, TimeUnit-enhet) — som den forrige metoden, blokkerer denne metoden tråden som kalte den, og venter på resultatet, men bare for tiden spesifisert av metodeparameterne.
-
boolean cancel(boolean mayInterruptIfRunning) — denne metoden prøver å stoppe utførelsen av oppgaven. Hvis oppgaven ikke har begynt å kjøre ennå, vil den aldri kjøre. Hvis oppgaven var i gang, bestemmer mayInterruptIfRunning- parameteren om det vil bli gjort et forsøk på å avbryte tråden som utfører oppgaven. Etter at kanselleringsmetoden er kalt, vil isDone- metoden alltid returnere true .
-
boolean isCancelled() — denne metoden returnerer true hvis oppgaven avbrytes før den fullføres normalt. Metoden vil alltid returnere true hvis kanselleringsmetoden tidligere ble kalt og returnert true .
Eksempel på kode som bruker Callable og 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;
}
}
La oss kjøre følgende hovedmetode :
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);
}
}
Konsollen vil vise:
Fibonacci nummer 17: 987
Fibonacci nummer 18: 1597
Fibonacci nummer 19: 2584
Fibonacci nummer 10: 34
Fibonacci nummer 11: 55 Fibonacci
nummer 12: 89
Fibonacci
nummer 134ci nummer 1341
nummer 1341 15:377
GO TO FULL VERSION