4 Eylül 2019 Çarşamba

Java Singleton Örnekleri

Giriş
Singleton ile yapılamaması gereken bazı şeyler şöyle.

1. Singleton reflection ile yaratılmamalı
2. Singleton Clone yapılamamalı
3. Singleton Serializable olmamalı

1 - Static Alan Yöntemi
Açıklaması şöyle.
In eager initialization, the instance of Singleton Class is created at the time of class loading, this is the easiest method to create a singleton class but it has a drawback that instance is created even though client application might not be using it.
Bu yöntem en basit olanı
Örnek
Şöyle yaparız.
class Single {

  private final static Single sing = new Single();       
  private Single() {
  }        
  public static Single getInstance() {
    return sing;
  }
}
Singleton'dan kalıtım da olabilir. Ama ne işe yarar hayal edemiyorum. Şöyle yaparız.
class XSingleton extends Singleton {

}
2 - Static Constructor Yöntemi
Örnek
Şöyle yaparız. Yine static alan kullanılıyor, tek farkı sınıf yüklenince çalışan static constructor kullanılması.
public class SingletonConnection   {


  private static Connection connection = null ;
  static 
  {
    connection = ...;
  }
  public static Connection getConnection() throws Exception {
    return connection;
  }
}
3 - Holder Yöntemi
Holder Singleton yazısına taşıdım.

4. Static ve Syncronized Yöntemi
Şöyle yaparız. Yine static alan kullanılıyor, tek farkı staticalanın ilk getInstance() çağrısında ilklendirilmesi.
private static YourObject instance;

public static synchronized YourObject getInstance() {
    if (instance == null) {
        instance = new YourObject();
    }
    return instance;
}
5. Enum Yöntemi
Bu yöntemde enum şeklinde bir sınıf kullanılıyor. Java 1.5'ten sonra bu yöntemin kullanılması tavsiye ediliyor. Enum olduğu için sınıf otomatik olarak Serializable da oluyor. Bu yöntemin JVM'in kendi içinde kullandığı bir kilit ile bu nesneyi yarattığı dolayısıyla thread-safe olduğu belirtilmiş. Joshua Bloch şöyle yazıyor.
you get an ironclad guarantee that there can be no instances besides the declared constants. The JVM makes this guarantee, and you can depend on it.
Şöyle yaparız.
public enum UserActivity {
  INSTANCE;

  public void dostuff() {
    ...
  }
}

// use it as ...
UserActivity.INSTANCE.doStuff();

6. Double Checked Locking
Eğer multi thread ortamda lazy singleton kullanmak istersek şöyle yaparız. Nesne volatile tanımlanmalıdır! Volatile tanımlanınca "partially initialized object" sıkıntısı olmuyor. Yani 
single == null kontrolü ve single = new ... satırları yer değiştirmiyor.
class MultithreadedSingle {        
  private static volatile MultithreadedSingle single;       
  private MultithreadedSingle() {
  }        
  public static MultithreadedSingle getInstance() {
    if(single==null){
      synchronized(MultithreadedSingle.class){
        if(single==null){
          single= new MultithreadedSingle(); 
        }      
      }
    }
    return single;
  }
}
Eğer lock olarak sınıfı kullanmak istemezsek şöyle yaparız.
private static final Object lock = new Object();
private static volatile YourObject instance;

public static YourObject getInstance() {
    YourObject r = instance;
    if (r == null) {
        synchronized (lock) {    // While we were waiting for the lock, another 
            r = instance;        // thread may have instantiated the object.
            if (r == null) {  
                r = new YourObject();
                instance = r;
            }
        }
    }
    return r;
}
Şimdi niçin synhronized kelimesinden sonra ikinci defa null kontrolü yaptığımıza bakalım.
if (_instance == null) {                // L1
    synchronized (Singleton.class) {    // L2
        if (_instance == null) {        // L3
            _instance = new Singleton();// L4
        }
    }
}
Eğer ikinci defa null kontrolü yapmazsak iki thread şöyle çalışır. Yani bloke olan ikinci thread, synchronized bloğa girince, birinci thread'in yaptığı işi kontrol etmezse, yeni bir nesne yaratır
1. Thread 1 L1'e gelir ve _instance null'dır
2. Thread 2 L1'e gelir ve _instance null'dır
3. Thread 1 L2'de mutex'i kilitler
4. Thread 2 L2'de mutex'i kilitlemeye çalışır ama bloke olur
5. Thread 1 yeni bir instance yaratır ve L4'te atar
6. Thread 1 L2'de kilitlediği mutex'i bırakır
7. Thread 2 L2'de mutex'i kilitler
8. Thread 2 yeni bir instance yaratır ve L4'te atar
9. Thread 2 L2'de kilitlediği mutex'i bırakır
Dolayısıyla hata olur. synchronized kelimesinden sonra ikinci null kontrolü Thread 2'nin yeni bir instance yaratmasını engeller. İşte bu yüzden ismi "Double Checked"

Yanlış bir kod şöyle
class SharedObject {

  private static final Object LOCK = new Object(); //Volatile değil

  private static SharedObject instance = null;

  public static SharedObject retrieve() {
    if (instance == null) {
      synchronized (LOCK) {
        //Burada ikinci null kontrolü yapılmıyor
        instance = create();
      }  
    } 
    return instance;
  }
   
  private static SharedObject create() {
    return new SharedObject();
  }
}
7 - ClassLoader ile beraber private static final field
Bu yöntem Java 1.5'ten önce kullanılıyor. Final bir sınıf ve içinde private static final bir nesne yaratılıyor. private static final olduğu için JVM nesneyi lazy ve thread-safe olarak yaratır. Serialization
public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}





Hiç yorum yok:

Yorum Gönder