Java’da CompletableFuture ile Asenkron Programlama
Java’da asenkron programlama, özellikle büyük veri işleme veya uzun süren işlemleri gerçekleştirmek için oldukça kullanışlıdır. Bu sayede, bir uygulamanın uzun süren işlemler sırasında diğer işlemleri de yürütebilmesi sağlanır. Java 8 ile birlikte sunulan CompletableFuture, asenkron işlemleri yönetmek için güçlü bir araçtır. Bu yazıda, CompletableFuture
‘ün ne olduğunu, nasıl kullanıldığını ve Java’da asenkron iş akışlarını yönetmenin avantajlarını detaylı bir şekilde ele alacağız.
CompletableFuture Nedir?
CompletableFuture, Java’da asenkron işlemleri gerçekleştirmek ve yönetmek için kullanılan bir sınıftır. CompletableFuture
, bir sonucu gelecek bir tarihte tamamlanabilecek bir işlemi temsil eder ve bu sayede asenkron iş akışlarını kolaylıkla oluşturmanızı sağlar. Bu araç, özellikle paralel ve birbirine bağımlı işlemleri zincirleme ve yönetme konusunda büyük bir esneklik sunar.
CompletableFuture ile Temel Asenkron İşlem
CompletableFuture
kullanarak bir işlemi asenkron olarak başlatmak oldukça basittir. Aşağıdaki örnekte, asenkron olarak bir hesaplama işlemi yapacağız.
Örnek: CompletableFuture Kullanımı
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000); // 2 saniyelik bekleme simülasyonu
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "İşlem Tamamlandı";
});
System.out.println("Asenkron işlem başlatıldı...");
try {
String result = future.get(); // İşlemin tamamlanmasını bekle ve sonucu al
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
Bu örnekte, supplyAsync()
metodu ile asenkron bir işlem başlatılıyor ve sonuç daha sonra get()
metodu ile alınıyor. Bu sayede diğer işlemler beklemeden devam edebilir.
CompletableFuture Metotları
CompletableFuture
, zincirleme işlemler ve asenkron işlemleri kolayca yönetebilmeniz için çeşitli metotlar sunar. Bunlardan bazıları şunlardır:
- thenApply(): Bir işlem tamamlandıktan sonra, sonucunu başka bir işleme dönüştürmek için kullanılır.
- thenAccept(): İşlem tamamlandıktan sonra sonucu kullanarak bir işlem yapmak için kullanılır. Geri dönüş değeri yoktur.
- thenRun(): İşlem tamamlandıktan sonra, sonucu kullanmadan bir işlem yapmak için kullanılır.
- thenCombine(): İki farklı
CompletableFuture
sonucunu birleştirip tek bir sonuç üretmek için kullanılır. - thenCompose(): Bir
CompletableFuture
‘ün sonucuna dayalı olarak yeni birCompletableFuture
oluşturmak için kullanılır.
thenApply() ve thenAccept() Kullanımı
Bir işlem tamamlandıktan sonra yeni bir işlem yapmak istiyorsanız thenApply()
veya thenAccept()
metodunu kullanabilirsiniz.
Örnek: thenApply() ve thenAccept() Kullanımı
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 50)
.thenApply(num -> num * 2) // 50'yi iki ile çarp
.thenApply(result -> result + 10); // Sonuca 10 ekle
try {
System.out.println("Sonuç: " + future.get()); // Sonuç: 110
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> "Merhaba, Dünya!")
.thenAccept(System.out::println); // Sonucu yazdır
try {
future2.get(); // Sonucu al ve yazdır
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
Bu örnekte, thenApply()
kullanarak bir değeri iki ile çarptık ve ardından sonuca 10 ekledik. thenAccept()
ise sonucu yazdırmak için kullanılmıştır.
thenCombine() ile Birden Fazla CompletableFuture Birleştirme
Bazen iki veya daha fazla asenkron işlemin sonucunu birleştirmek gerekebilir. thenCombine()
metodu bu tür durumlar için idealdir.
Örnek: thenCombine() Kullanımı
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 30);
CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, (num1, num2) -> num1 + num2);
try {
System.out.println("Birleştirilmiş Sonuç: " + combinedFuture.get()); // Birleştirilmiş Sonuç: 50
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
Bu örnekte, future1
ve future2
adlı iki farklı CompletableFuture
‘ün sonucunu birleştirerek toplamını elde ettik.
Asenkron Hatalar ve Exception Handling
CompletableFuture
kullanırken, asenkron işlemler sırasında oluşabilecek hataları ele almak önemlidir. exceptionally()
metodu, bir CompletableFuture
başarısız olduğunda hatayı ele almanıza olanak tanır.
Örnek: exceptionally() Kullanımı
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
if (true) {
throw new RuntimeException("Bir hata oluştu!");
}
return 42;
}).exceptionally(ex -> {
System.out.println("Hata: " + ex.getMessage());
return 0;
});
try {
System.out.println("Sonuç: " + future.get()); // Hata: Bir hata oluştu! Sonuç: 0
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
Bu örnekte, supplyAsync()
içinde bir istisna oluştuğunda exceptionally()
metodu bu hatayı ele alır ve varsayılan bir değer döner.
CompletableFuture ve Paralel İşlemler
CompletableFuture
, aynı anda birden fazla işlemi başlatmanıza ve bu işlemleri bağımsız olarak çalıştırmanıza olanak tanır. Bu tür bir kullanım, büyük miktarda veri işleme veya uzun süren işlemler için oldukça faydalıdır.
Örnek: Paralel Asenkron İşlemler
import java.util.concurrent.CompletableFuture;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class Main {
public static void main(String[] args) {
List<CompletableFuture<String>> futures = List.of(
CompletableFuture.supplyAsync(() -> "İşlem 1 tamamlandı"),
CompletableFuture.supplyAsync(() -> "İşlem 2 tamamlandı"),
CompletableFuture.supplyAsync(() -> "İşlem 3 tamamlandı")
);
CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
try {
allOf.get(); // Tüm işlemler tamamlanana kadar bekle
futures.forEach(future -> {
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
Bu örnekte, CompletableFuture.allOf()
metodu ile birden fazla asenkron işlemin tamamlanmasını bekledik ve ardından sonuçları yazdırdık.
Bu makalede, Java’da CompletableFuture kullanarak asenkron programlama ve zincirleme işlemleri nasıl yöneteceğimizi detaylı bir şekilde ele aldık. CompletableFuture
, Java geliştiricileri için güçlü bir araçtır ve asenkron işlemleri yönetmeyi oldukça kolay hale getirir.
- Java Concurrency ve Multithreading Bölüm 5: Asenkron Akış ve Reactive Programming
- Java Concurrency ve Multithreading Bölüm 4: CompletableFuture ile Asenkron İşlemler
- Milyon Dolarlık Yazılım Projesi: PHP ile Adım Adım Geliştirme
- Java Concurrency ve Multithreading Bölüm 3: ExecutorService ve Thread Havuzları
- Milyon Dolarlık Bir Proje Kodluyoruz