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 bir CompletableFuture 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.