---
title: Senkronizasyon
source: 'https://academia.sh/tr/kurslar/jvm-ve-eszamanlilik/senkronizasyon'
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'
---

# Senkronizasyon

İzleyici kilidine yeniden girmek tutma sayısını 0'dan 2'ye çıkarıp geri indiriyor. Aynı senkronize yöntem aynı örnekte çağrıldığında tek kilit, iki ayrı örnekte ya da bir örnek yöntemiyle bir sınıf yönteminde çağrıldığında iki ayrı kilit devreye giriyor; üç kurulumun ikisinde kilit sayısı ikiye çıkıyor.

Önceki ders `synchronized` bloğunu tabloya tek bir satır olarak koydu ve okuyanla yazanın
**aynı** kilidi kullanması gerektiğini söyledi — ama o derste `kilit` tek bir statik alandı,
iki iş parçacığının aynı nesneyi paylaştığı baştan verilmişti. Bu ders o varsayımı kaldırıyor.
`synchronized` anahtar sözcüğü kaynakta hangi kilidin kullanılacağını açıkça yazmaz; bir
örnek yöntemi çağrıldığında kullanılan kilit **o çağrıdaki nesneye** bağlıdır ve aynı kaynak
satırı, hangi nesne üzerinden çağrıldığına göre iki ayrı kilide karşılık gelebilir.

Bu belirsizlik iki ayrı soruyu birbirinden ayırmayı gerektiriyor. Birincisi: bir iş parçacığı
şu an bir kilidi tutuyor mu, ve tutuyorsa kaç kez? Bu, tek bir iş parçacığının kendi
durumuyla ilgili bir sorudur ve bu dersin ilk bölümü bunu ölçüyor. İkincisi: iki ayrı çağrı
**aynı** kilidi mi kullanıyor? Bu, kaynak metne bakarak değil, iki çağrının hangi nesne
üzerinden yapıldığına bakarak yanıtlanabilir bir sorudur ve dersin ikinci bölümü bunu üç
kurulumda sayıyor.

## İzleyici Kilidini Kim Tutuyor

- **EZ9** — `Thread.holdsLock(nesne)` yalnız çağıran iş parçacığının o nesnenin kilidini o an
  tutup tutmadığını sorar; başka bir iş parçacığının tutup tutmadığını sormaz, bu bilgi API
  ile hiç sunulmaz. Bir kilidin **kaç kez** tutulduğu ise `ReentrantLock.getHoldCount()` ile
  ölçülür — izleyici kilidinin kendisi bu sayıyı dışarı vermez, bu yüzden ölçüm açık kilitle
  yapılıyor.

```java
// KilitTutma.java — izleyici kilidini kim tutuyor holdsLock ile, yeniden girmenin tutma sayisi ReentrantLock ile olculur
import java.util.concurrent.locks.ReentrantLock;

public class KilitTutma {
    static final Object izleyici = new Object();
    static final ReentrantLock acikKilit = new ReentrantLock();

    public static void main(String[] args) {
        System.out.println("kilit tutuluyor mu (baslamadan once) : " + Thread.holdsLock(izleyici));
        synchronized (izleyici) {
            System.out.println("kilit tutuluyor mu (blok icinde)      : " + Thread.holdsLock(izleyici));
        }
        System.out.println("kilit tutuluyor mu (bloktan sonra)    : " + Thread.holdsLock(izleyici));

        System.out.println();
        System.out.println("baslamadan once     tutmaSayisi=" + acikKilit.getHoldCount());
        acikKilit.lock();
        try {
            System.out.println("1. giristen sonra   tutmaSayisi=" + acikKilit.getHoldCount());
            acikKilit.lock(); // ayni is parcacigi ayni kilide yeniden giriyor
            try {
                System.out.println("2. giristen sonra   tutmaSayisi=" + acikKilit.getHoldCount());
            } finally {
                acikKilit.unlock();
                System.out.println("ic cikistan sonra   tutmaSayisi=" + acikKilit.getHoldCount());
            }
        } finally {
            acikKilit.unlock();
            System.out.println("dis cikistan sonra  tutmaSayisi=" + acikKilit.getHoldCount());
        }
    }
}
```

```
kilit tutuluyor mu (baslamadan once) : false
kilit tutuluyor mu (blok icinde)      : true
kilit tutuluyor mu (bloktan sonra)    : false

baslamadan once     tutmaSayisi=0
1. giristen sonra   tutmaSayisi=1
2. giristen sonra   tutmaSayisi=2
ic cikistan sonra   tutmaSayisi=1
dis cikistan sonra  tutmaSayisi=0
```

İlk üç satır kilidin **kimliğini** ölçüyor: `synchronized (izleyici)` bloğuna girildiği an
`holdsLock` `true`'ya dönüyor, bloktan çıkıldığı an `false`'a geri düşüyor — kilit tutmanın
kendisi blok sınırına bağlı, ayrıca bir bırakma çağrısı gerektirmiyor. Alttaki beş satır ise
**yeniden girilebilirliği** ölçüyor. `ReentrantLock`, adını da buradan alıyor: aynı iş
parçacığı zaten tuttuğu kilide ikinci kez girdiğinde engellenmiyor, tutma sayısı `1`'den
`2`'ye çıkıyor. İç bloktan çıkıldığında kilit **hâlâ tutuluyor** — sayı `1`'e düşüyor, sıfıra
değil — ve ancak dış bloktan da çıkıldığında sayı `0`'a iniyor. Bu, izleyici kilidinin de
(built-in `synchronized`) taşıdığı ama dışarı sayı olarak vermediği bir özelliktir: bir
yöntem kendi çağırdığı başka bir senkronize yöntemde aynı kilide yeniden girdiğinde
kilitlenmiyor, çünkü kilit **iş parçacığına** aittir, çağrıya değil — aynı iş parçacığı kendi
tuttuğu kilidi tekrar isteyebiliyor.

Yeniden girilebilirlik olmasaydı, senkronize bir yöntemin kendi sınıfındaki başka bir
senkronize yöntemi çağırması **kendi kendini kilitlemek** anlamına gelirdi — bir iş
parçacığı, zaten sahip olduğu bir kilidi ikinci kez istediği için sonsuza kadar beklerdi. Bu,
tek bir iş parçacığı içinde oluşabilecek en küçük kilitlenme biçimidir ve tutma sayısının
varlığı tam olarak bunu önlüyor: çerçeve her `lock()` çağrısında "bu kilit zaten benim mi"
diye soruyor, yanıt evetse engellemiyor, sayıyı artırıp geçiyor. `holdsLock`'ın kendisi bu
sayıyı dışarı vermiyor, çünkü sorduğu soru farklı — yalnız "tutuyor muyum" diye soruyor,
"kaç kez tutuyorum" diye sormuyor; iki soru da bu derste ayrı araçlarla yanıtlanıyor.

## Kilit Nesneye Aittir, Koda Değil

- **EZ10** — Üç kurulumun üçü de aynı iki satırlık günlük desenini kullanıyor: bir iş
  parçacığı girdiğinde `"X girdi"` yazıyor, çıkarken `"X cikti"` yazıyor. Günlüğün **sırası**,
  hangi kilidin paylaşıldığını kanıtlıyor — süre hiçbir yerde ölçülmüyor.

```java
// Kutu.java — kilit nesneye aittir, koda degil: senkronize yontem ornek kilidini, static yontem sinif kilidini kullanir
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class Kutu {
    synchronized void gir(List<String> gunluk, String etiket, CountDownLatch girdi, CountDownLatch devamEt)
            throws InterruptedException {
        gunluk.add(etiket + " girdi");
        if (girdi != null) girdi.countDown();
        if (devamEt != null) devamEt.await();
        gunluk.add(etiket + " cikti");
    }

    static synchronized void sinifGir(List<String> gunluk, String etiket) {
        gunluk.add(etiket + " girdi (sinif kilidi)");
        gunluk.add(etiket + " cikti (sinif kilidi)");
    }
}
```

`gir`, kendini çağıran iş parçacığını isteğe bağlı olarak durdurabiliyor: `girdi` verilmişse
girer girmez haber veriyor, `devamEt` verilmişse serbest bırakılana kadar kilidi elinde
tutuyor. Üç kurulum bu iki kolu farklı biçimde kullanıyor.

```java
// KacAyriKilit.java — uc kurulumda kac ayri kilit devreye giriyor
import java.util.*;
import java.util.concurrent.CountDownLatch;

public class KacAyriKilit {
    static List<String> kurulum1AyniOrnek() throws InterruptedException {
        Kutu kutu = new Kutu();
        List<String> gunluk = Collections.synchronizedList(new ArrayList<>());
        CountDownLatch girdiA = new CountDownLatch(1);
        CountDownLatch bekleA = new CountDownLatch(1);

        Thread a = new Thread(() -> { try { kutu.gir(gunluk, "A", girdiA, bekleA); } catch (InterruptedException e) { } });
        a.start();
        girdiA.await(); // A kilidi aldi ve tutuyor

        Thread b = new Thread(() -> { try { kutu.gir(gunluk, "B", null, null); } catch (InterruptedException e) { } });
        b.start(); // B ayni kilidi istiyor, A birakmadikca giremez

        bekleA.countDown(); // A'yi serbest birak
        a.join();
        b.join();
        return gunluk;
    }

    static List<String> kurulum2FarkliOrnek() throws InterruptedException {
        Kutu kutu1 = new Kutu();
        Kutu kutu2 = new Kutu();
        List<String> gunluk = Collections.synchronizedList(new ArrayList<>());
        CountDownLatch girdiA = new CountDownLatch(1);
        CountDownLatch bekleA = new CountDownLatch(1);
        CountDownLatch bittiB = new CountDownLatch(1);

        Thread a = new Thread(() -> { try { kutu1.gir(gunluk, "A", girdiA, bekleA); } catch (InterruptedException e) { } });
        a.start();
        girdiA.await(); // A, kutu1'in kilidini tutuyor

        Thread b = new Thread(() -> {
            try { kutu2.gir(gunluk, "B", null, null); bittiB.countDown(); }
            catch (InterruptedException e) { }
        });
        b.start();
        bittiB.await(); // B, A hala beklerken tamamlandi -- ayri bir kilit kullaniyor demektir

        bekleA.countDown();
        a.join();
        b.join();
        return gunluk;
    }

    static List<String> kurulum3OrnekVeSinif() throws InterruptedException {
        Kutu kutu = new Kutu();
        List<String> gunluk = Collections.synchronizedList(new ArrayList<>());
        CountDownLatch girdiA = new CountDownLatch(1);
        CountDownLatch bekleA = new CountDownLatch(1);
        CountDownLatch bittiB = new CountDownLatch(1);

        Thread a = new Thread(() -> { try { kutu.gir(gunluk, "A", girdiA, bekleA); } catch (InterruptedException e) { } });
        a.start();
        girdiA.await(); // A, ornek kilidini tutuyor

        Thread b = new Thread(() -> { Kutu.sinifGir(gunluk, "B"); bittiB.countDown(); });
        b.start();
        bittiB.await(); // B, A hala beklerken tamamlandi -- sinif kilidi ayri

        bekleA.countDown();
        a.join();
        b.join();
        return gunluk;
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("1. ayni ornek uzerinde iki cagri:");
        System.out.println("   " + kurulum1AyniOrnek());
        System.out.println("2. iki ayri ornek uzerinde iki cagri:");
        System.out.println("   " + kurulum2FarkliOrnek());
        System.out.println("3. ornek yontemi ve sinif yontemi:");
        System.out.println("   " + kurulum3OrnekVeSinif());
    }
}
```

```
1. ayni ornek uzerinde iki cagri:
   [A girdi, A cikti, B girdi, B cikti]
2. iki ayri ornek uzerinde iki cagri:
   [A girdi, B girdi, B cikti, A cikti]
3. ornek yontemi ve sinif yontemi:
   [A girdi, B girdi (sinif kilidi), B cikti (sinif kilidi), A cikti]
```

Her üç kurulumda da A, `bekleA.countDown()` çağrılana kadar kilidi bırakmıyor. Birinci
kurulumda B'nin günlüğü **A tamamen çıktıktan sonra** başlıyor — B, `gir` yönteminin ilk
satırına bile ulaşamıyor, çünkü senkronize yöntemin gövdesine girmek için önce aynı kilidi
alması gerekiyor ve o kilit A'da. İkinci ve üçüncü kurulumlarda ise B'nin **hem girişi hem
çıkışı**, A henüz kilidi bırakmadan günlüğe düşüyor — B'nin tamamlanması A'nın bırakmasını hiç
beklemiyor. Bu, iki ayrı kilidin devrede olduğunun doğrudan kanıtı: eğer aynı kilit
paylaşılıyor olsaydı, B de birinci kurulumdaki gibi A'nın çıkışını beklemek zorunda kalırdı.

Kanıtın gücü tam olarak burada yatıyor: hiçbir satır "iki ayrı kilit var" diye iddia
etmiyor, yalnız bir sıralama gözleniyor ve o sıralama başka türlü açıklanamıyor. B'nin
`bittiB.countDown()` çağırabilmiş olması, `gir` ya da `sinifGir` yönteminin gövdesini
**tamamen** çalıştırmış olduğu anlamına geliyor; bu da o gövdeye giren senkronize bloğun
kilidini almış ve bırakmış olduğu anlamına geliyor. A ise aynı anda kendi kilidini hâlâ
tutuyor — `bekleA` henüz düşmedi. İki iş parçacığı da bir kilit tutarken üçüncü kurulumda
kilitlenme oluşmuyor, çünkü iki kilit ayrı nesnelere ait; birinci kurulumda ise B'nin
`girdi` parametresi `null` olduğu için B'nin ne zaman kilide girdiğine dair bağımsız bir
sinyal yok — ama günlükteki sıra zaten tek olasılığı bırakıyor.

| Kurulum | Çağrılar | Ayrı kilit sayısı |
|---|---|---|
| 1. aynı örnek | `kutu.gir` × 2 | 1 |
| 2. iki ayrı örnek | `kutu1.gir`, `kutu2.gir` | 2 |
| 3. örnek + sınıf yöntemi | `kutu.gir`, `Kutu.sinifGir` | 2 |

Üç kurulumun **ikisinde** ayrı kilit sayısı ikiye çıkıyor. Örnek kilidi çağrıldığı nesneye
bağlıyken, sınıf kilidi tek bir `Class` nesnesine bağlıdır ve bütün örnekler arasında
paylaşılır — ama örnek kilidiyle **aynı** kilit değildir. Bir sınıfın hem örnek hem statik
senkronize yöntemi varsa, aynı anda bir iş parçacığı örnek yöntemindeyken başka bir iş
parçacığı statik yöntemi çağırabilir; ikisi birbirini hiç engellemez.

Bu ayrım, `synchronized` kullanırken hangi nesnenin kilit olarak seçildiğinin **tasarım
kararı** olduğunu gösteriyor. `Kutu.gir` örtük olarak `this` üzerinde kilitleniyor — bu, bir
sınıfın senkronize yöntemleri için varsayılan davranış. Aynı kilit, sınıfın dışından erişilen
bir referanssa (`this` genel bir API'nin parçasıysa), sınıfın hiç bilmediği bir çağıran da
`synchronized (kutu) { ... }` yazarak aynı kilide talip olabilir ve içeriden beklenmeyen bir
çekişme yaratabilir. `Kutu.gir` bu dersin ölçümü için `this`'i kullanıyor, ama üçüncü kurulum
zaten bunun tersini de gösteriyor: örnek kilidi ile sınıf kilidi birbirinden bağımsız olduğu
gibi, bir sınıf kendi özel bir kilit nesnesini de kullanabilir — o zaman dışarıdan hiçbir
çağıran o kilide `synchronized` ile ulaşamaz, çünkü referansın kendisi hiç dışarı çıkmaz.

## Sınırlayıcı Ölçüm: Kilit Sıra Vermez

- **EZ11** — Bu bölümde bekleyen iş parçacıklarının **hangi sırayla** kilide gireceği
  koşturularak ölçülmüyor; ölçülseydi sonucu çizelgeleyiciye bağlı olurdu ve bu, kursun
  kurallarına aykırı bir yarış koşulu koşturmak olurdu.

Yukarıdaki üç kurulumun hepsinde yalnız iki iş parçacığı vardı ve hangisinin bekleyeceği
baştan biliniyordu. Aynı kilidi isteyen **üçüncü, dördüncü** bir iş parçacığı eklendiğinde,
kilit serbest kalınca bekleyenlerden **hangisinin** gireceği `synchronized`'ın kendisi
tarafından belirtilmez — karşılıklı dışlamayı garanti eder, sırayı garanti etmez.
`ReentrantLock`'ın kurucusuna `true` verilerek talep edilen **adil kilit** seçeneği bekleme
sırasına yaklaşmayı hedefler, ama bu ders o sırayı bir koşumdan ölçmüyor; seçeneğin varlığı
ve amacı burada yalnız **modelleniyor**. Aynı kilidi isteyen çok sayıda iş parçacığının
birbirini süresiz bekletmesi ayrı bir kusurdur ve M01/K05 İşletim Sistemi Kavramları kursunda kavram
olarak zaten kuruldu; bu ders onu yeniden kurmuyor, yalnız kilidin sıra **vermediğini** ölçüm
dışı bırakarak hatırlatıyor.

Adil kilidin neden varsayılan olmadığı da modellenen bir gerekçedir, koşulan bir sayı değil:
sırayı zorlamak, her `lock()` çağrısında bekleme kuyruğunun başını kontrol etmeyi
gerektiriyor, ve bu kontrol yalnız bekleyen varsa değil **her seferinde** işliyor. `synchronized`
ve `ReentrantLock`'ın adil olmayan biçimi, boşta bir kilide yeni gelen bir çağrının kuyruğu
hiç yoklamadan doğrudan girmesine izin veriyor. Bu ders bu iki seçenek arasındaki maliyet
farkını ölçmüyor, çünkü ölçmek demek gerçek bir sürüm karşılaştırması yapmak demek olurdu;
tek belirttiği, sıranın **isteğe bağlı** bir garanti olduğu ve varsayılanın bunu vermediğidir.
Bir kilidi bekleyen iş parçacıklarının sayısı arttıkça hangi tasarımın seçileceği bu dersin
sorusu değil — bu ders yalnız, seçim yapılmadığı sürece hangi garantinin **olmadığını**
sabitliyor.

Üçüncü kurulumdaki sınıf kilidi de kendi içinde tekildir: `Kutu` sınıfının **bütün** statik
senkronize yöntemleri aynı `Class` nesnesinin kilidini paylaşır, tıpkı bir örneğin bütün
örnek senkronize yöntemlerinin o örneğin kilidini paylaşması gibi. Bir sınıfın iki ayrı statik
senkronize yöntemi aynı anda iki ayrı iş parçacığından çağrılsaydı, bu kurulum onları da
birinci kurulumdaki gibi sıraya sokardı — orada ölçülmedi, çünkü bu dersin sorusu zaten örnek
kilidiyle sınıf kilidinin **birbirinden ayrı** olup olmadığıydı, sınıf kilidinin kendi içinde
tekil olup olmadığı değil.

## Özet

- `Thread.holdsLock` bir kilidin çağıran tarafından o an tutulup tutulmadığını belirlenimci
  biçimde söyler; `ReentrantLock.getHoldCount()` yeniden girme derinliğini sayar ve iç içe iki
  girişte `0`'dan `2`'ye çıkıp geri `0`'a iner.
- Bir kilit iş parçacığına aittir, çağrıya değil: aynı iş parçacığı zaten tuttuğu kilide
  yeniden girdiğinde engellenmez.
- Örnek kilidi çağrıldığı **nesneye** bağlıdır: aynı senkronize yöntem iki ayrı örnekte
  çağrıldığında iki ayrı kilit kullanılır; sınıf kilidi bunlardan **ayrı üçüncü** bir kilittir.
- Üç kurulumun yalnız birinde (aynı örnek üzerindeki iki çağrı) tek kilit paylaşılıyor;
  kalan ikisinde ayrı kilit sayısı ikiye çıkıyor.
- Kilit karşılıklı dışlama ve görünürlük sağlar, bekleyenler arasında sıra vermez; sıra
  isteniyorsa adil kilit seçeneği modellenir, koşumdan ölçülmez.

## Sonraki Adım

Bu üç kurulumda da kilidi elle almak ve bırakmak çağıranın işiydi — `synchronized` bloğu ya
da `acikKilit.lock()`/`unlock()` çifti her seferinde elle yazıldı, ve her yazımda kilidin
serbest bırakılmasını garanti etmek `finally` bloğuna düştü. `KacAyriKilit`'teki her üç
kurulum da bu deseni tekrarladı; gerçek bir programda bu tekrar, unutulan bir `unlock()`
çağrısının kilidi süresiz tuttuğu bir kusura dönüşebilir. Sıradaki ders bu elle tutma işini
standart kitaplığın hazır tiplerine devrediyor: bir sayaç kendi kilidini kendi yönetip
yönetmediğini, bir mandal bekleyeni ne zaman serbest bıraktığını, bir liste yazarken kimin
ödediğini ölçüyor — kilit artık kaynak metinde görünmüyor, ama iş hâlâ birinin üzerine
düşüyor; sıradaki soru bu yükün hangi tarafa yüklendiği.
