A futtatható probléma
Már ismeri a Runnable felületet és az azt megvalósító Thread osztályt. Emlékezzünk vissza, hogyan néz ki ez a felület:
public interface Runnable {
public abstract void run();
}
Vegye figyelembe, hogy a futtatási metódus visszatérési típusa érvénytelen . De mi van akkor, ha arra van szükségünk, hogy a szálunk visszaadja munkájának valamilyen eredményét szám, karakterlánc vagy más objektum formájában? Akkor ki kell találnunk egy megoldást. Valami ilyesmi:
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);
}
}
Futtassuk a következő fő módszert:
public static void main(String[] args) throws Exception {
Fibonacci.printByIndex(10);
}
A konzol a következőket jeleníti meg:
Ennek a kódnak számos hátránya van. Például a join metódus hívása következtében a fő szál blokkolódik, miközben a printByIndex metódus végrehajtásra kerül.
Hívható felület
Most pedig nézzük meg a Java által nekünk kínált felületet, amely a Runnable alternatívájaként használható . Ez a hívható felület:
public interface Callable<V> {
V call() throws Exception;
}
Mint látható, a Runnable-hoz hasonlóan ennek is csak egy módszere van. Ez a metódus ugyanazt a célt szolgálja, mint a futtatási metódus – tartalmazza a párhuzamos szálban végrehajtandó kódot. Ami a különbségeket illeti, nézze meg a visszatérési értéket. Mostantól bármilyen típusú lehet, amelyet az interfész implementálásakor ad meg:
public class CurrentDate implements Callable<Long> {
@Override
public Long call() {
return new Date().getTime();
}
}
Egy másik példa:
Callable<String> task = () -> {
Thread.sleep(100);
return "Done";
};
Van még valami hasznos – a hívási módszer kivételt adhat . Ez azt jelenti, hogy a run metódussal ellentétben a call metódusban nem kell kezelnünk a metóduson belül előforduló ellenőrzött kivételeket:
|
|
Jövő interfész
Egy másik felület, amely szorosan együttműködik a Callable szolgáltatással, a Future . A jövő az aszinkron (párhuzamos) számítások eredményét jelenti (a hívási metódus által visszaadott érték). Lehetővé teszi annak ellenőrzését, hogy a számítások megtörténtek-e, megvárhatja a számítások befejezését, lekérheti a számítások eredményét stb.
A jövő interfész módszerei
-
logikai érték isDone() — ez a metódus igazat ad vissza , ha ez a feladat (számítás) elkészült. Azok a feladatok, amelyek normálisan végződtek, kivétellel végződtek vagy töröltek, késznek tekintendők.
-
V get() — ha szükséges, ez a metódus blokkolja az őt hívó szálat, és visszaadja a számítások eredményét, amikor azok elkészültek.
-
V get(long timeout, TimeUnit unit) — az előző metódushoz hasonlóan ez a metódus is blokkolja az őt hívó szálat, és várja az eredményt, de csak a metódus paraméterei által megadott ideig.
-
logikai cancel(boolean mayInterruptIfRunning) — ez a metódus megpróbálja leállítani a feladat végrehajtását. Ha a feladat még nem indult el, akkor soha nem fog futni. Ha a feladat folyamatban volt, akkor a mayInterruptIfRunning paraméter határozza meg, hogy megkísérlik-e megszakítani a feladatot végrehajtó szálat. A cancel metódus meghívása után az isDone metódus mindig true értékkel tér vissza .
-
logikai érték isCancelled() – ez a metódus igazat ad vissza , ha a feladatot a normál befejezés előtt megszakítják. A metódus mindig igazat ad vissza, ha a cancel metódust korábban meghívták, és igazat adott vissza .
Példa kódra a Callable és Future használatával
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;
}
}
Futtassuk a következő fő módszert:
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);
}
}
A konzol a következőket jeleníti meg:
Fibonacci szám 17: 987
Fibonacci szám 18: 1597
Fibonacci szám 19: 2584
Fibonacci szám 10: 34
Fibonacci szám 11: 55 Fibonacci
szám 12: 89
Fibonacci szám 12: 89 Fibonacci
szám 243 43:
15:377
GO TO FULL VERSION