İçeriğe geç

Singleton Tasarım Kalıbı – Lazy Loading & Double Checked Locking

Merhaba arkadaşlar,

Bu videoda singleton tasarım kalıbının farklı sorunları ve tabiki bu sorunlara üretilen çözümleri öğrenmeye devam edeceğiz.

Önceki videoda tasarım ile, bu nesne hiç kullanılmayacak olsa bile bir adet singleton nesnesi oluşturmuş oluyor. Kullanılmasını beklemeden class lar oluşurken yapılan bu yüklemeye eager loading deniyor.

Bu da büyük projeler için farklı probleme neden olabilir. Bu problemi aşmak istersek ne gibi bir önlem alabiliriz?

Ne zaman ihtiyaç duyarsak, ihtiyaç duyduğumuz ilk anda bu nesneyi oluştururuz ve sonraki tüm ihtiyaçlarımızda ilk oluşturduğumuz ne ise onu kullanmaya devam ederiz. Yani lazy loading yapmış oluruz.

Peki biz nesneye ne zaman ihtiyaç duyulduğunu nasıl anlayacağız? Nesneye ihtiyaç duyulması demek, nesnenin getSingleton metodunun çağrılması demek. Yani nesneyi burada oluşturabiliriz. İlk olarak sadece bu dediğimi uyguluyorum ve nesne yi newleme işleminin global değişkende değil de bu metodun içersinde yapıyorum

public class Singleton {

    private static Singleton singleton;

    private static int sayi = 0;

    private Singleton() {
        System.out.println("Ben oluştum");
    }

    public static Singleton getSingleton() {
        
        singleton = new Singleton();

        sayi++;

        System.out.println(sayi);

        return singleton;
    }
}

Şimdi uygulamayı çalıştırıyorum.

Tasarımımız hepten bozuldu. Bu problemi aşmak için ise zaten global olarak tutuğum değişkenin null olup olmamasını kontrol edebiliriz. Eğer null ise oluştur, null değilse var olanı ver deriz. Yapalım.

public class Singleton {

    private static Singleton singleton;

    private static int sayi = 0;

    private Singleton() {
        System.out.println("Ben oluştum");
    }

    public static Singleton getSingleton() {

        if (singleton == null){
            singleton = new Singleton();
        }

        sayi++;

        System.out.println(sayi);

        return singleton;
    }
}

Şimdi uygulamamızı çalıştıralım.

Görüldüğü gibi sadece bir kere oluşmasını sağladık ve bunu classlar yüklenirken değil de ihtiyaç duyulduğunda yapmayı başardık.

Peki bitti mi? Bitti gibi duruyor fakat bitmedi. Biz hep tek thread li olarak düşündük ve kodumuzu ona göre yazdık. Peki ya multi thread bir ortamda çalışıyorsak ve aynı anda getSingleton metodu çağrılırsa? O zaman ikisi için de null olacak ve ikisi de birbirinden ayrı nesneler oluşturabilecek. Lazy loading yapalım derken çok ciddi bir açık vermiş olduk.

Bu büyük problemi nasıl çözeriz? Bu metodu tread safe hale getiren bir anahtar sözcük vardır. Bu sözcük synchronized anahtar sözcüğüdür. Metodun başına synchronized eklersek bu sorunu çözeriz. Ekleyelim.

public class Singleton {

    private static Singleton singleton;

    private static int sayi = 0;

    private Singleton() {
        System.out.println("Ben oluştum");
    }

    public static Singleton getSingleton() {

        synchronized (Singleton.class) {
            if (singleton == null) {
                singleton = new Singleton();
            }
        }

        sayi++;
        System.out.println(sayi);

        return singleton;
    }
}

Maalesef bunun da bir problemi var. Evet synchronized metodu thread safe hale getirdi fakat metodun çalışmasını çok ciddi şekilde yavaşlattı da. Neden yavaşlattığından da kısaca bahsedelim. 4 farklı thread ile 4 koldan iş yapıyor olalım. Metotlar kendi işlerini yaparlarken de synchronized anahtar sözcüğü ile karşılaştıklarını düşünelim. Bu sözcükle karşılaştıkları yerlerde, tread ler kuyruğa girerler. Şöyle de düşünebiliriz. Boğaz köprüsüne 4 yoldan bağlantı sağlanıyor fakat köprü 1 tane olduğundan, her biri birbirini beklemeye başlıyor ve trafik oluşuyor. Bu nedenle olabildiğince az synchronized ile karşılaşacak şekilde tasarım yapmalıyız.

Bunun için de aslında çok basit bir çözüm sunabiliriz. Bizler synchronized a ne zaman ihtiyaç duyuyoruz. Nesnenin ilk oluşumu esnasında. Yani tek derdimiz nesne oluşurken senkron çalışması. Sonraki seferlerde zaten elimizdeki nesneyi döneceğiz. Dolayısı ile sadece bir null kontrolü ekleyerek büyük bir sorunu daha ortadan kaldırmış oluyoruz.

public class Singleton {

    private static Singleton singleton;

    private static int sayi = 0;

    private Singleton() {
        System.out.println("Ben oluştum");
    }

    public static Singleton getSingleton() {

        if (singleton == null){
            synchronized (Singleton.class){
                if (singleton == null){
                    singleton = new Singleton();
                }
            }
        }

        sayi++;
        System.out.println(sayi);

        return singleton;
    }
}

Bu 2 li null kontrollü tasarım kalıbına double checked locking kalıbı denir. Bu kalıp sayesinde Hem lazy loading, Hem thread safe hem de maksimum Performans ile bir tasarım yapmış oluruz.

Tarih:JavaTasarım Kalıpları

İlk Yorumu Siz Yapın

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir