ExecutorService ve CompletableFuture ile asenkron iş nasıl yapılır? Thread pool boyutunu nasıl belirlersiniz?
Cevap
ExecutorService, thread'leri tek tek yaratmak yerine bir thread pool'a iş göndermeyi sağlar. CompletableFuture ise bu işleri zincirleyip birleştirmeyi (callback, hata yönetimi, paralel bekleme) kolaylaştırır.
ExecutorService pool = Executors.newFixedThreadPool(16);
CompletableFuture<User> userF =
CompletableFuture.supplyAsync(() -> userClient.get(id), pool);
CompletableFuture<List<Order>> ordersF =
CompletableFuture.supplyAsync(() -> orderClient.list(id), pool);
CompletableFuture<Profile> profileF = userF
.thenCombine(ordersF, Profile::new)
.orTimeout(2, TimeUnit.SECONDS)
.exceptionally(ex -> Profile.empty());
Profile p = profileF.join();thenApply: sonucu dönüştürür;thenCompose: başka bir future döndüren adımı düzleştirir (flatMap).allOf(...): hepsini bekler;anyOf(...): ilk biteni alır.exceptionally/handle: hata yönetimi.
Tuzaklar
- Executor vermezseniz
supplyAsyncForkJoinPool.commonPool() kullanır; bu havuz varsayılan olarakCPU sayısı - 1thread içerir ve bloklayan I/O ile hızla tıkanır. I/O işleri için kendi executor'ınızı verin. Executors.newCachedThreadPool()sınırsız thread,newFixedThreadPoolsınırsız kuyruk kullanır. Production'da sınırlı kuyruk ve reddetme politikası olanThreadPoolExecutortercih edin.- Pool'u uygulama kapanırken
shutdown()+awaitTermination()ile kapatın.
Pool boyutu
- CPU-bound: yaklaşık çekirdek sayısı (
Runtime.getRuntime().availableProcessors()). - I/O-bound:
çekirdek * (1 + bekleme süresi / hesaplama süresi). Örneğin 8 çekirdek, isteklerin %90'ı beklemeyse yaklaşık 80 thread. - Sonuçta yük testi ile doğrulanır; downstream servisin (DB pool, API limiti) kapasitesi de üst sınırdır.
Java 21+ ile bloklayan I/O işleri için Executors.newVirtualThreadPerTaskExecutor() çoğu zaman pool boyutu hesabını gereksiz kılar.
İlgili Java & Spring soruları
- Java 21 virtual thread'ler nedir? Platform thread'lerden farkı nedir ve hangi durumlarda dikkatli olmak gerekir?
- JPA/Hibernate'te lazy ve eager loading farkı nedir? LazyInitializationException neden oluşur ve nasıl çözülür?
- @Transactional anotasyonu hangi durumlarda çalışmaz? Self-invocation, rollback kuralları ve propagation tuzaklarını açıklayın.
- Spring bean scope'ları nelerdir? Singleton bir bean'e prototype bean inject edilirse ne olur?
- Spring'de Dependency Injection nedir? Constructor injection neden field injection'a tercih edilir?
- Java'da Garbage Collection nasıl çalışır? G1 ve ZGC arasındaki farklar nelerdir, hangisini ne zaman seçersiniz?
Bu konuda başka bir soru biliyor musun?
Mülakatta karşılaştığın soruları ekle, onaylandıktan sonra yayına alınsın.