---
title: 'Eşzamansız Bileşim'
source: 'https://academia.sh/tr/kurslar/jvm-ve-eszamanlilik/eszamansiz-bilesim'
course: 'JVM, Eşzamanlılık ve Başarım'
language: tr
updated: '2026-08-23T07:00:56+00:00'
license: 'CC BY-SA 4.0'
---

# Eşzamansız Bileşim

Gelecek tabanlı bir zincirde her aşamayı hangi tarafın koşturduğu — tamamlanmış gelecekte çağıran, bekleyen gelecekte tamamlayan taraf, sonekli çağrıda hiç oluşturmadığımız ya da verilen bir havuz — ve aynı zincirin dört yazımda dört ayrı tarafta koşup sonucun dördünde de aynı kaldığı ölçülür. Bir aşama düşerse sonraki aşamaların atlandığı ve hatanın join'de çağırana sarmalanmış geldiği gösterilir.

Önceki ders bağımsız görevleri ucuzlattı; bu ders onları bir **zincire** diziyor. Bir
aşamanın sonucu bir sonrakinin girdisi oluyor ve her halka kendi başına doğru çalışsa da
zincire dizildiğinde yeni bir soru doğuyor: bu aşamayı hangi iş parçacığı koşturuyor? Gelecek
(`future`) tabanlı zincirleme bu soruyu her bağlama noktasında yeniden soruyor, ve kursun
ölçtüğü dört yanıt burada aynı zincir üzerinde bir arada görünüyor.

## Aynı Zincir Dört Yazımda Dört Ayrı Tarafta Koşuyor

Bir aşama, bağlandığı `CompletableFuture` zaten tamamlanmışsa bağlama çağrısını yapan iş
parçacığında **senkron** koşuyor; gelecek henüz tamamlanmamışsa aşama, geleceği tamamlayan
tarafın iş parçacığında koşuyor. `Async` sonekli bir bağlama ise üçüncü bir yolu açıyor:
yürütücü verilmezse ortak bir havuzda, verilirse verilen yürütücüde.

**EZ26.** Bir aşama, bağlandığı gelecek zaten tamamlanmışsa bağlama çağrısını yapan iş
parçacığında senkron koşar; gelecek henüz tamamlanmamışsa aşama, geleceği tamamlayan iş
parçacığında koşar.
**EZ27.** `Async` sonekli bir aşama, yürütücü verilmezse programın hiç oluşturmadığı ortak bir
havuzda (bilinmeyen), yürütücü verilirse verilen yürütücünün iş parçacığında (oluşturulan)
koşar.

```java
// Kosum.java — bir adimi hangi is parcacigi kosturuyor: cagiran mi, olusturulan mi, hic olusturmadigimiz bir taraf mi (yardimci kaynak, main yok)
import java.util.*;

class Kosum {
    static final Map<Thread, String> etiketler = Collections.synchronizedMap(new IdentityHashMap<>());

    static void tanit(String etiket) { etiketler.put(Thread.currentThread(), etiket); }

    static String kim() {
        String e = etiketler.get(Thread.currentThread());
        return e == null ? "bilinmeyen" : e;
    }

    static void gozle(String nokta) {
        System.out.printf("%-32s -> %s%n", nokta, kim());
    }
}
```

```java
// DortYazim.java — ayni zincir dort ayri yazimda dort ayri tarafta kosuyor, sonuc dordunde de ayni
import java.util.*;
import java.util.concurrent.*;

public class DortYazim {
    static int asamaFonksiyonu(int v) { return v * 2 + 1; }

    public static void main(String[] args) throws Exception {
        Kosum.tanit("cagiran");
        List<Integer> sonuclar = new ArrayList<>();

        // A. tamamlanmis gelecek: adim cagiranda kosuyor
        int a = CompletableFuture.completedFuture(10)
                .thenApply(v -> { Kosum.gozle("tamamlanmis gelecek adimi"); return asamaFonksiyonu(v); })
                .join();
        sonuclar.add(a);

        // B. bekleyen gelecek: adimi tamamlayan is parcacigi kosturuyor
        CompletableFuture<Integer> bekleyen = new CompletableFuture<>();
        CompletableFuture<Integer> zincir = bekleyen.thenApply(v -> {
            Kosum.gozle("bekleyen gelecek adimi"); return asamaFonksiyonu(v);
        });
        Thread tamamlayan = new Thread(() -> { Kosum.tanit("olusturulan"); bekleyen.complete(10); });
        tamamlayan.start();
        tamamlayan.join();
        sonuclar.add(zincir.join());

        // C. varsayilan havuzda async: hic olusturmadigimiz bir is parcaciginda
        int c = CompletableFuture.completedFuture(10)
                .thenApplyAsync(v -> { Kosum.gozle("varsayilan havuzda async"); return asamaFonksiyonu(v); })
                .join();
        sonuclar.add(c);

        // D. verilen havuzda async: kurdugumuz yuruticunun is parcaciginda
        ExecutorService verilen = Executors.newSingleThreadExecutor(
                gorev -> new Thread(() -> { Kosum.tanit("olusturulan"); gorev.run(); }));
        int d = CompletableFuture.completedFuture(10)
                .thenApplyAsync(v -> { Kosum.gozle("verilen havuzda async"); return asamaFonksiyonu(v); }, verilen)
                .join();
        sonuclar.add(d);
        verilen.shutdown();

        boolean hepsiAyni = sonuclar.stream().distinct().count() == 1;
        System.out.println("dort yazimin sonucu (10*2+1=21)  : " + sonuclar);
        System.out.println("hepsi ayni mi                    : " + hepsiAyni);
    }
}
```

```
tamamlanmis gelecek adimi        -> cagiran
bekleyen gelecek adimi           -> olusturulan
varsayilan havuzda async         -> bilinmeyen
verilen havuzda async            -> olusturulan
dort yazimin sonucu (10*2+1=21)  : [21, 21, 21, 21]
hepsi ayni mi                    : true
```

Dört satır kursun dört ayrı yanıtını tek zincirde topluyor. Tamamlanmış bir gelecekte
`thenApply` bağlanır bağlanmaz koşuyor, çünkü koşacak bir şey zaten hazır — bağlama çağrısını
yapan iş parçacığı hangisiyse aşama onda koşuyor: **çağıran** (EZ26). Bekleyen bir gelecekte
aşama beklemede kalıyor; `bekleyen.complete(10)` çağrısı gelene kadar hiçbir iş
parçacığında koşmuyor, çağrı geldiğinde de onu **çağıran** iş parçacığında koşuyor — burada
o taraf `tamamlayan` adıyla kurduğumuz iş parçacığı, yani **oluşturulan** (EZ26). Sonek eklenen
üçüncü satırda yürütücü verilmediği için aşama ortak bir havuzda koşuyor; bu havuzu biz ne
kurduk ne adlandırdık, bu yüzden **bilinmeyen** (EZ27). Dördüncü satırda aynı sonek bu kez bir
yürütücüyle veriliyor ve aşama o yürütücünün — bizim kurduğumuz — iş parçacığında koşuyor:
**oluşturulan** (EZ27).

## Sonek Eklemek Koşturan Tarafı Değiştirir, Hızlandırmaz

Dört yazımın da uyguladığı işlem birebir aynı: `v -> v * 2 + 1`, aynı başlangıç değeri
üzerinde. Sonuç listesi `[21, 21, 21, 21]` — dördü de aynı sayıya varıyor. Bu, dersin
sınırlayıcı ölçümü: `thenApply` ile `thenApplyAsync` arasındaki fark, hesaplanan **değerde**
değil, o değeri **kimin** hesapladığında. Sonek eklemek işi hızlandırmadı; bu derste hiçbir
süre ölçülmedi ve ölçülemez de — değişen tek şey çağrı yazımının hangi tarafı seçtiği. Bir
aşamayı `Async` sonekiyle yazmanın gerekçesi hız değil, o aşamayı çağıranın iş parçacığından
**ayırmak**: örneğin çağıran bir arayüz iş parçacığıysa ya da uzun sürebilecek bir işi
kendi üzerinde tutmak istemiyorsa. Kararın kendisi bu dersin konusu değil; ölçülen yalnızca
kararın **sonucu**, yani hangi tarafın koştuğu.

## Bir Aşama Düşünce Sonrakiler Atlanıyor

Zincirdeki bir aşama istisna fırlatırsa ne olur? Sıradaki aşamalar hiç çağrılmıyor ve hata,
zincirin ucundaki `join()` çağrısına kadar taşınıyor.

**EZ28.** `thenApply` zincirinde her aşama bir öncekinin tamamlandığı iş parçacığında senkron
koşar; bu zincirde hiçbir `Async` soneki yok, dolayısıyla hata yolunun hangi iş parçacığında
koştuğu ayrıca bir kurguya ihtiyaç duymaz — zaten tamamlanmış bir gelecekte bu iş parçacığı
çağırandır.

```java
// HataYolu.java — bir asama duserse sonraki asamalar atlaniyor, hata join'de cagirana sarmalanmis geliyor
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

public class HataYolu {
    public static void main(String[] args) throws Exception {
        AtomicInteger calisanAsama = new AtomicInteger(0);

        CompletableFuture<Integer> zincir = CompletableFuture.completedFuture(10)
                .thenApply(v -> { calisanAsama.incrementAndGet(); return v + 1; })
                .thenApply(v -> { if (v > 0) throw new RuntimeException("asama hatasi"); return v; })
                .thenApply(v -> { calisanAsama.incrementAndGet(); return v + 1; })
                .thenApply(v -> { calisanAsama.incrementAndGet(); return v + 1; });

        try {
            zincir.join();
            System.out.println("zincir dusmedi");
        } catch (CompletionException e) {
            System.out.printf("%-32s -> %s sarmaliyor: %s%n", "hata join'de yakalandi",
                    e.getClass().getSimpleName(), e.getCause().getClass().getSimpleName());
        }

        int hatadanSonrakiTanimliAsama = 2;
        int hatadanSonrakiCalisanAsama = calisanAsama.get() - 1;
        System.out.printf("%-32s -> %d%n", "hatadan once calisan asama", 1);
        System.out.printf("%-32s -> %d%n", "hatadan sonra tanimli asama", hatadanSonrakiTanimliAsama);
        System.out.printf("%-32s -> %d%n", "hatadan sonra atlanan asama",
                hatadanSonrakiTanimliAsama - hatadanSonrakiCalisanAsama);
    }
}
```

```
hata join'de yakalandi           -> CompletionException sarmaliyor: RuntimeException
hatadan once calisan asama       -> 1
hatadan sonra tanimli asama      -> 2
hatadan sonra atlanan asama      -> 2
```

Zincir dört aşamadan kuruluydu: birincisi normal koştu, ikincisi bir istisna fırlattı,
üçüncü ve dördüncüsü hiç koşmadı. Sayaç bunu doğruluyor — hatadan önce **bir** aşama
çalıştı, hatadan sonra tanımlı **iki** aşamanın **ikisi de** atlandı. `thenApply` zinciri
bir aşama düştüğünde geri kalanı otomatik olarak **düşmüş** sayıyor; her aşama kendinden
öncekinin başarıyla tamamlandığını varsayıyor, bu varsayım kırılınca zincirin geri kalanı
hiç değerlendirilmiyor. Hata `join()` çağrısına kadar sessizce taşınıyor ve orada
`CompletionException` içine sarmalanmış olarak çağırana ulaşıyor; asıl istisna
`getCause()` ile geri alınabiliyor. Bu davranış önceki dersin `submit`/`get` çiftiyle aynı
yönde: bir istisna, verildiği biçimin izin verdiği ilk noktaya kadar sessiz kalabiliyor.

İki dersin istisna taşıma biçimi de birbirine yakın ama özdeş değil. Önceki ders `Future.get()`
çağrıldığında istisnayı denetimli `ExecutionException` içine sarıyordu; bu ders `join()`
çağrıldığında aynı işi denetimsiz `CompletionException` ile yapıyor. İkisi de gerçek istisnayı
`getCause()` ile geri veriyor, ikisi de görevin/aşamanın çalıştığı taraftan çağıranın
tarafına bir sarmalamayla geçiyor — fark yalnız hangi çağrının (`get` mi `join` mi) hangi
sarmalamayı (denetimli mi denetimsiz mi) seçtiğinde.

## Özet

- Bir aşama, bağlandığı gelecek zaten tamamlanmışsa bağlama çağrısını yapan iş parçacığında
  senkron koşar; gelecek henüz tamamlanmamışsa aşama, geleceği tamamlayan iş parçacığında
  koşar.
- `Async` sonekli bir aşama yürütücü verilmezse programın hiç oluşturmadığı ortak bir
  havuzda, verilirse verilen yürütücünün iş parçacığında koşar.
- Aynı zincir dört ayrı yazımla kurulduğunda dört ayrı taraf koşturur, ama uygulanan işlem
  aynı olduğu için sonuç dördünde de birebir aynı kalır; sonek eklemek koşturan tarafı
  değiştirir, hızlandırmaz.
- Bir aşama istisna fırlattığında zincirdeki sonraki bütün aşamalar atlanır; hata `join()`
  çağrısına kadar sessizce taşınır ve orada `CompletionException` içine sarmalanmış olarak
  çağırana ulaşır.
- Kesilen bir zincirde hatadan önceki aşamalar normal biçimde çalışmış olur; atlanan aşama
  sayısı zincirin geri kalanının uzunluğuna eşittir.

## Kurs Kapanışı

**EZ29.** Kapanış tablosundaki koşturan taraf sütunu yalnızca üç değeri alır: çağıran,
oluşturulan, bilinmeyen — bunlar ortak tanımın kurduğu üç yanıttır ve kurs boyunca sabit
kalır.

Kurs tek bir soruyla açıldı: bu adımı hangi iş parçacığı koşturuyor, ve o iş parçacığını kim
oluşturdu? On bir ders bu soruyu on bir ayrı düzenekte sordu ve yanıtı her seferinde en az
iki koşum yolunu yan yana koşturarak aldı. Birinci iddia doğrulandı ve bu derste bir kez daha
görüldü: koşturan iş parçacığı çağrı yazımında görünmüyor — `thenApply` ile `thenApplyAsync`
arasındaki fark bir sonek, ama koşturan taraf üçe ayrılıyor. İkinci iddia — programın hiç
oluşturmadığı bir iş parçacığının kendi kodunu koşturabilmesi — bu derste ortak havuzla bir
kez daha örneklendi. Üçüncü iddia, K04'e özgü ayrım olan "kim koşturuyor" ile "kim görüyor"
sorusunun ayrılığı, önceki derslerde bellek modeli ve `ThreadLocal` üzerinden ölçülmüştü;
bu ders o ayrımı sınamadı, yalnız koşturan tarafı sınadı. Kursun sınırı da baştan beri
aynıydı ve burada bir daha doğrulandı: sonek eklemek, havuz büyütmek, sanal iş parçacığı
kullanmak — hiçbiri tek başına bir hız iddiası değil; ölçülen her şey bir **sayıydı**, bir
**süre** değil.

| Ders | Ölçülen davranış | Koşturan taraf (çağıran · oluşturulan · bilinmeyen) | Sınırlayıcı ölçüm |
|---|---|---|---|
| Sınıf Yükleme | Dört tetikleyicinin üçü (yeni örnek, statik yöntem, statik alan okuma) sınıf başlatmasını koşturur; aynı sınıfa aynı anda dokunan iki iş parçacığından yalnız biri başlatma bloğunu çalıştırır | çağıran (dört tetikleyici) · oluşturulan (eş zamanlı dokunmada kazanan) | Derleme zamanı sabiti okumak sınıfı hiç başlatmaz; boş satır "tetiklenmedi" demektir, "olmadı" değil |
| Bellek Alanları | Altı değerden üçü (yerel değişken, parametre, iş parçacığına özel değer) yazan iş parçacığının dışına hiç çıkmaz, üçü (nesne alanı, dizi ögesi, statik alan) çıkar | oluşturulan (yazan) · çağıran (okuyan, `join` sonrası) | Paylaşılan olmak görülecek olmak değildir; `join` gibi bir sıralama aracı olmadan aynı okuma tanımsızdır |
| Çöp Toplayıcılar | Güçlü referans dururken üç toplama isteğinin üçünde de temizleme koşmaz; referans kaldırılınca koşar. Zayıf ve hayalet referans kuyruğa düşer, yumuşak direnir | bilinmeyen (temizleme eylemi) | `System.gc()` bir istektir; ölçüm temizlemenin ne zaman koşacağını değil, yalnız kimin koşturduğunu saptar |
| Anında Derleme | Aynı yöntem üç yüz bin kez çağrıldıktan sonra da sınıf dosyasındaki komut listesi birebir aynı kalır; çağrı yerinden geçen alıcı tip sayısı birden ikiye çıkınca tek alıcılı varsayım geçersizleşir | çağıran (profil verisini üreten) · bilinmeyen (kararı veren) | Hız ölçülmez ve ölçülemez; sayılan tek şey komut sayısı ile alıcı tip sayısıdır, optimizasyondan dönüş modellenir |
| İş Parçacıkları ve Yaşam Döngüsü | Aynı `Runnable` gövdesi `run()` ile çağıranda, `start()` ile oluşturulan iş parçacığında koşar; üç kesme kurulumundan yalnız biri kesme isteği olmadan gövdesini bitiremez | çağıran (`run`) · oluşturulan (`start`) | `start()` çağrısının dönmesi gövdenin koştuğunu göstermez; sıra `CountDownLatch` ve `join` ile elle kurulur |
| Java Bellek Modeli | Sekiz yayımlama düzeneğinden dördü hem yazanın hem okuyanın harekete geçmesini istiyor, `final` alan yükü yalnız yazana, `join` yükü yalnız okuyana yüklüyor, ikisi (senkronizasyonsuz alan, `ThreadLocal`) görünürlük sorusunun dışında kalıyor | oluşturulan (yazan) · çağıran (okuyan) | Senkronizasyonsuz bir alanın görünürlük kusuru koşturularak gösterilmez, kural olarak modellenir; `volatile` atomiklik vermez |
| Senkronizasyon | Yeniden girme derinliği `0`'dan `2`'ye çıkıp `0`'a iner; üç kilit kurulumunun yalnız birinde tek kilit paylaşılır, kalan ikisinde ayrı kilit sayısı ikiye çıkar | çağıran (kilidi tutan) · oluşturulan (bekleyen) | Kilit karşılıklı dışlama ve görünürlük verir, bekleyenler arasında sıra vermez; adil kilit seçeneği modellenir |
| Eşzamanlılık Yardımcıları | Karşılaştır-ve-ata kurgulanmış çakışmada üç denemede sonuca ulaşır ve iş çağırandan hiç ayrılmaz; dört anlık görüntü sırasıyla 0, 1, 2 ve 3 öge görür, yazan taraf her değişiklikte yeni bir dizi üretir | çağıran (karşılaştır-ve-ata, gezinme) · oluşturulan (sayacı düşüren, izni bırakan) | Atomik tip yalnız tek değişkene atomiklik verir; iki değişkenin birlikte tutarlılığı koşturularak değil, kural olarak anlatılır |
| Yürütücü Servisleri | Havuz boşken kendi iş parçacığı, doluyken görevi veren taraf koşturur; reddetme yordamı çağıranın seçtiği bir parametredir | çağıran (dolu havuzda reddedilen) · oluşturulan (havuza verilen görev) | İki verme biçiminden yalnızca biri (`execute`) istisnayı kendiliğinden gösterir; `submit` yalnızca `get` çağrılırsa gösterir |
| Sanal İş Parçacıkları | Aynı görev kümesinin sabit havuzda dörtle sınırlı kalan, sanal yürütücüde görev sayısı kadar büyüyen ayrık kimliği; `ThreadLocal`'ın görev başına ayrı değer üretmesi | oluşturulan (her ikisinde de) | Ayrık kimlik sayısının büyümesi bir hız iddiası değil bir olgudur; taşıyıcı gözlemlenemez |
| Eşzamansız Bileşim | Tamamlanmış gelecekte çağıran, bekleyen gelecekte tamamlayan taraf, sonekli çağrıda bilinmeyen ya da verilen havuz koşar; hatadan sonraki aşamalar atlanır | çağıran (tamamlanmış) · oluşturulan (bekleyen, verilen havuz) · bilinmeyen (varsayılan havuz) | Sonek eklemek hızlandırmaz, yalnız koşturan tarafı değiştirir; dört yazımın sonucu birebir aynı kalır |

JVM, Eşzamanlılık ve Başarım burada kapanıyor. Kursun sorduğu soru — bu adımı hangi iş
parçacığı koşturuyor — bir Java programının her satırının altında hep durur; çağrı yazımı bu
soruyu hiçbir zaman kendiliğinden yanıtlamaz.
