Çalıştırılabilir sorunu
Runnable arayüzüne ve onu uygulayan Thread sınıfına zaten aşinasınız . Bu arayüzün neye benzediğini hatırlayalım:
public interface Runnable {
public abstract void run();
}
Çalıştırma yönteminin dönüş türünün geçersiz olduğunu unutmayın . Peki ya iş parçacığımızın çalışmasının bir sonucunu bir sayı, bir dizi ya da başka bir nesne biçiminde döndürmesine ihtiyacımız olursa? O zaman bir geçici çözüm bulmalıyız. Bunun gibi bir şey:
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);
}
}
Aşağıdaki ana yöntemi çalıştıralım :
public static void main(String[] args) throws Exception {
Fibonacci.printByIndex(10);
}
Konsol şunları gösterecektir:
Bu kodun birkaç dezavantajı vardır. Örneğin, join yöntemine yapılan çağrının bir sonucu olarak , printByIndex yöntemi yürütülürken ana iş parçacığı engellenir .
Çağrılabilir arayüz
Şimdi Java'nın kutudan çıkar çıkmaz bize sunduğu, Runnable'a alternatif olarak kullanılabilecek arayüze bakalım . Bu, Çağrılabilir arabirimdir:
public interface Callable<V> {
V call() throws Exception;
}
Gördüğünüz gibi Runnable gibi sadece bir metodu var. Bu yöntem, run yöntemiyle aynı amaca hizmet eder - paralel bir iş parçacığında yürütülecek kodu içerir. Farklılıklara gelince, dönüş değerine bir göz atın. Artık arayüzü uygularken belirttiğiniz herhangi bir tür olabilir:
public class CurrentDate implements Callable<Long> {
@Override
public Long call() {
return new Date().getTime();
}
}
Başka bir örnek:
Callable<String> task = () -> {
Thread.sleep(100);
return "Done";
};
İşte yararlı bir şey daha: call yöntemi bir İstisna atabilir . Bu, run yönteminden farklı olarak, call yönteminde, yöntem içinde oluşan kontrol edilen istisnaları işlememiz gerekmediği anlamına gelir:
|
|
Gelecek arayüzü
Callable ile yakın çalışan bir başka arayüz de Future'dır . Gelecek, eşzamansız (paralel) hesaplamaların sonucunu ( çağrı yöntemi tarafından döndürülen değer) temsil eder. Hesaplamaların yapılıp yapılmadığını kontrol etmenizi, hesaplamaların bitmesini beklemenizi, hesaplamaların sonucunu almanızı ve daha fazlasını sağlar.
Geleceğin Yöntemleri arayüzü
-
boolean isDone() — bu görev (hesaplama) yapılırsa bu yöntem true değerini döndürür. Normal bir şekilde sona eren, bir istisna ile sona eren veya iptal edilen görevler tamamlanmış sayılır.
-
V get() — gerekirse, bu yöntem onu çağıran iş parçacığını engeller ve bittiğinde hesaplamaların sonucunu döndürür.
-
V get(uzun zaman aşımı, TimeUnit birimi) — önceki yöntem gibi, bu yöntem de onu çağıran iş parçacığını engeller, sonucu bekler, ancak yalnızca yöntem parametreleri tarafından belirtilen süre boyunca.
-
boolean cancel(boolean mayInterruptIfRunning) — bu yöntem, görevin yürütülmesini durdurmaya çalışır. Görev henüz çalışmaya başlamadıysa, hiçbir zaman çalışmaz. Görev devam ediyorsa mayInterruptIfRunning parametresi, görevi yürüten iş parçacığını kesme girişiminde bulunulup bulunulmayacağını belirler. cancel yöntemi çağrıldıktan sonra , isDone yöntemi her zaman true değerini döndürür .
-
boolean isCancelled() — görev normal şekilde bitmeden önce iptal edilirse bu yöntem true değerini döndürür. cancel yöntemi daha önce çağrıldıysa ve true döndürüldüyse, yöntem her zaman true değerini döndürür .
Callable ve Future kullanan kod örneği
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;
}
}
Aşağıdaki ana yöntemi çalıştıralım :
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);
}
}
Konsol şunları gösterecektir:
Fibonacci sayısı 17: 987
Fibonacci sayısı 18: 1597
Fibonacci sayısı 19: 2584
Fibonacci sayısı 10: 34
Fibonacci sayısı 11: 55 Fibonacci sayısı 12:
89
Fibonacci sayısı 13: 144
Fibonacci sayısı 14: 233
Fibonacci sayısı 15: 377
GO TO FULL VERSION