it-swarm-tr.com

Java'da bir singleton deseni uygulamak için etkili bir yol nedir?

Java'da bir singleton deseni uygulamak için etkili bir yol nedir?

752

Enum kullanın:

public enum Foo {
    INSTANCE;
}

Joshua Bloch bu yaklaşımı Etkili Java Reloaded Google I/O 2008’de yaptığı konuşmada açıkladı: videoya bağlantı . Ayrıca sunumunun 30-32. Slaytlarına bakın ( effect_Java_reloaded.pdf ):

Serileştirilebilir Bir Singleton Uygulamanın Doğru Yolu

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Düzenleme: Bir "Etkili Java" nın çevrimiçi kısmı şöyle diyor:

"Bu yaklaşım, işlevsel olarak kamusal alan yaklaşımına eşdeğerdir, daha özlü olması dışında, seri hale getirme makinelerini ücretsiz sağlar ve sofistike serileştirme veya yansıtma saldırıları karşısında bile çoklu başlatmaya karşı demir kaplamalı bir garanti sunar. henüz yaygın olarak kabul edilmeksizin, bir tek elemanlı enum türü, bir tekli uygulamak için en iyi yoldur . "

768
Stephen Denne

Kullanıma bağlı olarak, birkaç "doğru" cevap vardır.

Java5'ten beri yapmanın en iyi yolu enum kullanmaktır:

public enum Foo {
   INSTANCE;
}

Java5 öncesi en basit örnek:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

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

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Hadi koddan geçelim. İlk önce, sınıfın final olmasını istersiniz. Bu durumda, kullanıcıların nihai olduğunu bilmeleri için final anahtar sözcüğünü kullandım. Daha sonra, kullanıcıların kendi Foo'larını oluşturmalarını engellemek için yapıcıyı özel yapmanız gerekir. Yapıcıdan bir istisna atmak, kullanıcıların ikinci bir Foo oluşturmak için yansıma kullanmasını önler. Ardından, tek örneği tutmak için bir private static final Foo alanı ve onu döndürmek için bir public static Foo getInstance() yöntemi oluşturun. Java özelliği, yapıcının yalnızca sınıf ilk kez kullanıldığında çağrılmasını sağlar.

Çok büyük bir nesneye veya ağır inşaat koduna sahipseniz VE ayrıca bir örneğe ihtiyaç duyulmadan önce kullanılabilecek başka erişilebilir statik yöntemlere veya alanlara sahipseniz, o zaman ve yalnızca o zaman tembel başlatma kullanmanız gerekir.

Örneği yüklemek için bir private static class kullanabilirsiniz. Kod daha sonra şöyle olurdu:

public final class Foo {

    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;
    }
}

private static final Foo INSTANCE = new Foo(); satırı yalnızca FooLoader sınıfı gerçekten kullanıldığında çalıştırıldığından, bu tembel başlatmaya dikkat eder ve iş parçacığının güvenli olması garanti edilir.

Nesnenizi serileştirmek de istediğinizde, seri kaldırma işleminin bir kopya oluşturmayacağından emin olmanız gerekir.

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;
    }
}

readResolve() yöntemi, nesnenin programınızın önceki çalışmasında serileştirildiği zaman bile tek örneğin döndürüleceğinden emin olur.

224
Roel Spilker

Feragatname: Müthiş cevapların hepsini henüz özetledim ve sözlerime yazdım.


Singleton uygularken 2 seçeneğimiz var
1. Yavaş yüklenme
2. Erken yükleme

Tembel yükleme, ek yükü ekler (çok dürüst olmak gerekirse), bu nedenle yalnızca çok büyük bir nesneye veya ağır inşaat koduna sahip olduğunuzda kullanın ve ayrıca bir örneğe ihtiyaç duyulmadan önce kullanılabilecek başka erişilebilir statik yöntem veya alanlara sahip olun; Eğer tembel başlatma kullanmanız gerekir. Aksi halde erken yüklemeyi seçmek iyi bir seçimdir.

Singleton uygulamanın en basit yolu 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

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

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

Erken yüklü singleton dışında her şey iyidir. Tembel yüklü singleton deneyelim

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

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

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

Şimdiye kadar çok iyi ama kahramanımız kahramanımızın birçok örneğini isteyen birden fazla kötü konu ile tek başına savaşırken hayatta kalmayacak.

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

ama kahramanı korumak için yeterli değil, Gerçekten !!! Kahramanımıza yardım etmek için yapabileceğimiz/yapmamız gereken en iyi şey budur 

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

Buna "Çift Kontrol Edilen Kilitleme deyimi" denir. Geçici ifadeyi unutmak kolaydır ve neden gerekli olduğunu anlamak zordur.
Detaylar için: http://www.cs.umd.edu/~pugh/Java/memoryModel/DoubleCheckedLocking.html

Şimdi kötü konu hakkında eminiz ama acımasız seri hale getirme ne olacak? Serialiazyon sırasında bile yeni bir nesne yaratılmadığından emin olmalıyız.

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

readResolve() yöntemi, nesnenin programımızın önceki bir çalışmasında serileştirildiği zaman bile tek örneğin döndürüleceğinden emin olacaktır.

Sonunda iş parçacıklarına ve serileştirmeye karşı yeterince koruma ekledik ancak kodumuz hantal ve çirkin görünüyor. Kahramanımıza bir makyaj yapalım

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

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

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

Evet bu bizim aynı kahramanımız :)
private static final Foo INSTANCE = new Foo(); satırı yalnızca FooLoader sınıfı gerçekten kullanıldığında çalıştırıldığından, bu tembel başlatmaya dikkat eder, 

ve diş güvenli olması garanti edilir.

Ve şimdiye kadar geldik, işte yaptığımız her şeyi başarmanın en iyi yolu, mümkün olan en iyi yoldur. 

 public enum Foo {
       INSTANCE;
   }

Hangi dahili olarak gibi ele alınacak 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

Artık seri hale gelme korkusu, iplikler ve çirkin kod değil. Ayrıca ENUMS singleton tembel olarak başlatılır

Bu yaklaşım, işlevsel olarak kamusal alan yaklaşımına eşittir, daha özlü olması dışında, serileştirme makineleri sağlar ücretsizdir ve çoklu .__ 'ya karşı ironclad garantisi sağlar. örneği, sofistike serileştirme karşısında bile. yansıma saldırıları. Bu yaklaşım henüz yaygın olarak benimsenmemiş olsa da, bir single-element enum tipi bir singleton uygulamanın en iyi yoludur.

-Joshua Bloch "Etkili Java" da 

Artık ENUMS'in neden Singleton'ı uygulamanın en iyi yolu olarak kabul edildiğini ve sabrınız için teşekkür ettiğinizi fark etmiş olabilirsiniz :)
blog adresinde güncellendi. 

127
xyz

Stu Thompson tarafından yayınlanan çözüm Java5.0 ve sonrasında geçerlidir. Ama kullanmamayı tercih ederim çünkü hataya açık olduğunu düşünüyorum.

Geçici ifadeyi unutmak kolaydır ve neden gerekli olduğunu anlamak zordur. Uçucu olmasaydı, bu kod çift kontrol kilitlemeli antipattern nedeniyle artık dişli güvenli olmazdı. Bununla ilgili daha fazla bilgi için bkz. Paragraf 16.2.4 - ygulamada Java Eşleşmesi . Kısacası: Bu şablon (Java5.0'dan önce veya geçici ifade olmadan) yanlış durumda (hala) olan Bar nesnesine bir başvuru verebilir.

Bu desen performans optimizasyonu için icat edildi. Ancak bu artık gerçek bir endişe değil. Aşağıdaki tembel başlatma kodunun okunması hızlı ve daha da önemlisi- kolaydır.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
123
Benno Richters

Java 5+ ile iş parçacığı güvenli:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT: Buradaki volatile değiştiricisine dikkat edin. :) Önemlidir çünkü onsuz, JMM (Java Bellek Modeli) tarafından değerindeki değişiklikleri görmek için diğer iş parçacıkları garanti edilmez. Senkronizasyon ile ilgilenmez - sadece o kod bloğuna erişimi seri hale getirir.

EDIT 2 : @ Bno 'nun cevabı Bill Pugh (FindBugs) tarafından önerilen yaklaşımı detaylandırıyor ve daha iyi tartışılabilir. Git oku ve cevabını da oyla.

94
Stu Thompson

Unut tembel başlatma , çok problemli. Bu en basit çözüm:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
90
Jonathan

Gerçekten ihtiyacın olduğundan emin ol. Buna karşı bazı argümanlar görmek için "singleton anti-pattern" için bir google yapın. Sanırım doğal olarak yanlış bir şey yok sanırım ama bu sadece bazı küresel kaynakları/verileri ortaya çıkarmak için bir mekanizma olduğundan, bunun en iyi yol olduğundan emin olun. Özellikle bağımlılık enjeksiyonunu daha yararlı buldum, özellikle ünite testleri kullanıyorsanız, çünkü DI test amaçlı olarak alaylı kaynakları kullanmanıza izin verir.

47
Neil Burroughs

Singleton'ı, onu yükleyen Classloader için yalnızca bir Singleton olduğunu unutmayın. Birden fazla yükleyici (Konteyner) kullanıyorsanız, her COULD kendi Singleton sürümüne sahiptir.

26
Javamann

DI'yi tekil olanları kullanmaya alternatif olarak öneren bazı cevaplarla gizemli davranıyorum; bunlar ilgisiz kavramlardır. Tekli veya tekli olmayan (örneğin, iş parçacığı başına) örnekleri enjekte etmek için DI kullanabilirsiniz. Spring 2.x kullanıyorsanız en azından bu doğru, diğer DI çerçeveleri için konuşamam.

Bu yüzden OP'ye cevabım (en önemsiz örnek kod hariç ama hepsi):

  1. Bahar gibi bir DI çerçeve kullanın, ardından
  2. Bağımlılıklarınızın tekil, ister kapsam, istek kapsamı veya herhangi bir şey olup olmadığını DI yapılandırmanızın bir parçası haline getirin.

Bu yaklaşım size, bir singleton kullanıp kullanmamanın kolay tersinir bir uygulama detayı olduğu (elbette kullandığınız herhangi bir singleton'un tabii ki olması koşuluyla) olduğu, ayrıştırılmış bir Nice (ve bu nedenle esnek ve test edilebilir) bir mimari sunar.

21
Andrew Swan

Gerçekten yazmadan önce neden bir singleton'a ihtiyacınız olduğunu düşünün. Java'da singletons'u kullandığınızda kolayca kullanabileceğiniz, bunların kullanımı hakkında yarı-dinsel bir tartışma var.

Şahsen ben, çoğu, bekarlığa veda tuğlasıyla tespit edilebilecek birçok nedenden ötürü tekillerden mümkün olduğunca sık kaçınmaya çalışıyorum. Tektonların çok sık suistimal edildiğini hissediyorum çünkü herkes tarafından anlaşılması kolay, "küresel" verileri bir OO tasarımına almak için bir mekanizma olarak kullanılıyorlar ve nesneyi doyurmaları kolay olduğu için kullanılıyor yaşam döngüsü yönetimi (ya da A'yı B içinden nasıl yapabildiğinizi gerçekten düşünün). Güzel bir orta alan için Kontrolün Ters Çevirilmesi (IoC) veya Bağımlılık Enjeksiyonu (DI) gibi şeylere bakın.

Gerçekten birine ihtiyacınız varsa, o zaman wikipedia bir singleton'ın düzgün bir şekilde uygulanmasına iyi bir örnektir.

20
Aidos

Aşağıda 3 farklı yaklaşım var

1) Enum

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) Çift kontrol Kilitleme/Tembel yükleme

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) Statik fabrika yöntemi

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}
16
Abhijit Gaikwad

Spring Framework'ü singletonlarımı yönetmek için kullanıyorum. Sınıfın "teklikliğini" zorlamaz (birden fazla sınıf yükleyici varsa bile gerçekten yapamazsınız) ancak farklı nesne türleri için farklı fabrikalar inşa etmek ve yapılandırmak için gerçekten kolay bir yol sağlar.

13
Matt

Versiyon 1:

public class MySingleton {
    private static MySingleton instance = null;
    private MySingleton() {}
    public static synchronized MySingleton getInstance() {
        if(instance == null) {
            instance = new MySingleton();
        }
        return instance;
    }
}

Tembel yükleme, iş parçacığı engelleme ile güvenli, synchronized nedeniyle düşük performans.

Versiyon 2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

Tembel yükleme, tıkanmaz, yüksek performans ile güvenli iplik.

11
coderz

Vikipedi'de ayrıca Java'da bulunan bazı örnekler singleton'lar vardır. Java 5 uygulaması oldukça eksiksiz görünüyor ve iş parçacığı için güvenli (çift kontrol kilitleme uygulanmış).

10
macbirdie

Eğer tembel yüklemeye ihtiyacınız yoksa hemen deneyin

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

Eğer tembel yükleme yapmak ve Singleton'ınızın iplik emniyetinde olmasını istiyorsanız, çift kontrol desenini deneyin. 

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

        public static Singleton getInstance() { 
              if(null == instance) {
                  synchronized(Singleton.class) {
                      if(null == instance) {
                          instance = new Singleton();
                      }
                  }
               }
               return instance;
        }

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

Çifte kontrol düzeninin çalışması garanti edilmediğinden (derleyicilerle ilgili bir sorun nedeniyle, bunun hakkında daha fazla bir şey bilmiyorum.), Ayrıca getInstance yönteminin tamamını senkronize etmeyi veya tüm Singleton'larınız için bir kayıt defteri oluşturmayı deneyebilirsiniz. 

10
Aleksi Yrttiaho

Enum singleton diyebilirim 

Java'da enum kullanan Singleton, genellikle enum singleton'ı bildirmenin bir yoludur. Enum singleton, örnek değişken ve örnek yöntem içerebilir. Basitlik uğruna, ayrıca, nesnenin durumunu etkiliyorsa, bu yöntemin iş parçacığı güvenliğini sağlamak için gerekenden daha iyi bir örnek yöntemi kullanıyorsanız, ayrıca unutmayın.

Bir enum kullanımı çok kolaydır ve başka şekillerde çevrilmesi gereken serileştirilebilir nesnelerle ilgili sakıncaları yoktur.

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Singleton'daki getInstance() yöntemini çağırmaktan çok daha kolay Singleton.INSTANCE ile erişebilirsiniz.

1.12 Enum Sabitlerinin Serileştirilmesi

Enum sabitleri sıradan serileştirilebilir veya dışa çıkarılabilir nesnelerden farklı serileştirilir. Bir enum sabitinin serileştirilmiş şekli yalnızca isminden oluşur; sabitin alan değerleri formda mevcut değildir. Bir enum sabitini serileştirmek için ObjectOutputStream, enum sabitinin name yönteminin döndürdüğü değeri yazar. Bir enum sabitini seri hale getirmek için, ObjectInputStream, sabit ismi akıştan okur; Ardından, seri hale getirilmiş sabit, Java.lang.Enum.valueOf yöntemini çağırarak elde edilir, sabitin enum türünü argümanlar olarak alınan sabit adla birlikte geçirir. Diğer serileştirilebilir veya haricilaştırılabilir nesneler gibi, enum sabitleri de daha sonra seri hale getirme akışında görünen geri referansların hedefleri olarak işlev görebilir.

Enum sabitlerinin seri hale getirildiği işlem özelleştirilemez: sınıflamaya özgü writeObject, readObject, readObjectNoData, writeReplace ve readResolve yöntemleri, enum tipleri tarafından seri hale getirme ve seri kaldırma sırasında göz ardı edilir. Benzer şekilde, herhangi bir serialPersistentFields veya serialVersionUID alan bildirimleri de yoksayılır - tüm enum türlerinde 0L sabit bir serialVersionUID bulunur. Serileştirilebilir alanları ve enum türleri için verileri belgelemek gereksizdir, çünkü gönderilen veri türünde bir değişiklik yoktur.

Oracle docs'tan alıntılandı

Geleneksel Singletons'la ilgili başka bir sorun ise, bir kez Serializable arabirimini uyguladığınızda, readObject() yöntemi her zaman Java'daki yapıcı gibi yeni bir örnek döndürdüğü için artık Singleton'da kalmamasıdır. Bu, readResolve() işlevini kullanarak ve yeni oluşturulan örneği aşağıdaki gibi singleton ile değiştirerek atmayı önleyebilir 

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

Singleton Class'ınız durumunuzu koruduğu takdirde, onları geçici hale getirmeniz gerektiğinden, bu daha da karmaşık bir hal alabilir, ancak Enum Singleton'da, Seri hale getirme JVM tarafından garanti edilir.


İyi okuma

  1. Singleton Deseni
  2. Enums'ler, Tekiller ve Serileştirme
  3. Çift kontrollü kilitleme ve Singleton düzeni
8
NullPoiиteя
There are 4 ways to create a singleton in Java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    }

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    }


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}
7
Dheeraj Sachan

Bu konuda oyuna biraz geç kalabilirsiniz, ancak bir singletonun uygulanmasında çok fazla nüans var. Tutucu şekli birçok durumda kullanılamaz. Bir uçucu kullanırken IMO - yerel bir değişken de kullanmalısınız. En baştan başlayalım ve problemi tekrarlayalım. Ne demek istediğimi anlayacaksın.


İlk girişim şu şekilde görünebilir:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

Burada INSTANCE adlı özel bir statik üyeye sahip olan MySingleton sınıfına ve getInstance () adlı bir genel statik yönteme sahibiz. GetInstance () işlevi ilk kez çağrıldığında, INSTANCE üyesi boş. Akış, yaratma koşuluna girecek ve MySingleton sınıfının yeni bir örneğini yaratacaktır. GetInstance () öğesine yapılan sonraki çağrılar, INSTANCE değişkeninin zaten ayarlanmış olduğunu ve bu nedenle başka bir MySingleton örneği oluşturmayacağını görecektir. Bu, tüm getInstance () arayanları arasında paylaşılan yalnızca bir MySingleton örneği olmasını sağlar.

Ancak bu uygulamanın bir sorunu var. Çok iş parçacıklı uygulamalar, tek örnek oluşturma konusunda bir yarış koşuluna sahip olacaktır. Birden fazla yürütme iş parçacığı, getInstance () yöntemine aynı anda (veya çevresinde) vurursa, her biri INSTANCE üyesini null olarak görür. Bu, her iş parçacığının yeni bir MySingleton örneği oluşturmasına ve ardından INSTANCE üyesini ayarlamasına neden olur.


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

Burada, getInstance () yöntemini senkronize etmek için yöntem imzasındaki senkronize edilmiş anahtar kelimeyi kullandık. Bu kesinlikle yarış koşullarımızı düzeltir. İplikler şimdi yöntemi tek tek bloke edecek ve girecektir. Ancak aynı zamanda bir performans sorunu yaratır. Bu uygulama yalnızca tek örneğin oluşturulmasını senkronize etmekle kalmaz, okurlar dahil olmak üzere tüm çağrıları getInstance () öğesine senkronize eder. INSTANCE değerini döndürdükleri için okumaların senkronize edilmesine gerek yoktur. Okurlar aramalarımızın büyük kısmını oluşturacaklarından (hatırlatma, yalnızca ilk aramada başlatmanın gerçekleşeceğini unutmayın), tüm yöntemi senkronize ederek gereksiz bir performansa maruz kalacağız.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

Burada senkronizasyonu yöntem imzasından MySingleton örneğinin oluşturulmasını saran senkronize bir bloğa taşıdık. Fakat bu bizim sorunumuzu çözüyor mu? Artık okumaları engellemiyoruz, ancak geriye doğru bir adım attık. Birden çok iş parçacığı, getInstance () yöntemini aynı anda veya aynı anda vuracak ve INSTANCE üyesini boş olarak göreceklerdir. Daha sonra birinin kilidi alacağı ve örneği yaratacağı senkronize bloğa vuracaklar. Bu iş parçacığı bloktan çıktığında, diğer iş parçacığı kilit için yarışacak ve her bir iş parçacığı birer birer blok boyunca düşecek ve sınıfımızın yeni bir örneğini oluşturacaktır. Yani başladığımız yere geri döndük.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Burada bloğun içinden INSIDE'dan başka bir çek daha veriyoruz. INSTANCE üyesi zaten ayarlanmışsa, başlatmayı atlarız. Buna çift kontrol kilitleme denir.

Bu bizim çoklu başlatma problemimizi çözer. Fakat bir kez daha, çözümümüz bir başka zorluk daha sundu. Diğer konular, INSTANCE üyesinin güncellendiğini “göremez”. Bunun nedeni, Java'nın bellek işlemlerini nasıl optimize ettiğidir. Konular, değişkenlerin orijinal değerlerini ana bellekten CPU önbelleğine kopyalar. Değerlerdeki değişiklikler daha sonra bu önbelleğe yazılır ve bundan okunur. Bu, performansı optimize etmek için tasarlanmış bir Java özelliğidir. Ancak bu, singleton uygulamamız için bir sorun yaratıyor. İkinci bir iş parçacığı - farklı bir önbellek kullanarak farklı bir CPU veya çekirdek tarafından işleniyor - ilk tarafından yapılan değişiklikleri görmez. Bu, ikinci iş parçacığının INSTANCE üyesini boş bırakılması için singleton'ımızın yeni bir örneğini oluşturmaya zorlar.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Bunu, INSTANCE üyesinin ilanındaki uçucu anahtar kelimeyi kullanarak çözüyoruz. Bu, derleyiciye CPU önbelleğini değil her zaman ana bellekten okumasını ve yazmasını söyleyecektir.

Ancak bu basit değişiklik bir bedeli var. CPU önbelleğini atlattığımız için, 4 kez yaptığımız geçici INSTANCE üyesinde çalıştığımız her defasında bir performans yakalayacağız. Varlığı iki kez kontrol ederiz (1 ve 2), değeri (3) ayarlıyoruz ve sonra değeri (4) döndürüyoruz. Birisi, bu yöntemin, yalnızca yöntemin ilk çağrısı sırasında örneği yarattığımız için, saçak durum olduğunu iddia edebilir. Belki de yaratılışın yarattığı bir performans tolere edilebilir. Ancak, asıl kullanım durumumuz bile, uçucu üye üzerinde iki kez çalışacağını söylüyor. Varlığını kontrol etmek için bir kez ve değerini döndürmek için tekrar.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

Performans isabeti, doğrudan geçici üyede çalışmaktan kaynaklandığı için, geçici bir değere yerel bir değişken ayarlayalım ve bunun yerine yerel değişkende çalışalım. Bu, uçucu madde üzerinde işlem yapma sayısını azaltacak ve böylece performansımızın bir kısmını geri alacaktır. Senkronize bloğa girdiğimizde yerel değişkenimizi tekrar ayarlamamız gerektiğini unutmayın. Bu, kilidi beklerken meydana gelen değişikliklerle güncel olmasını sağlar.Geçenlerde bunun hakkında bir makale yazdım. Singleton'un Yapısını Kaldırmak -. Bu örnekler hakkında daha fazla bilgi ve orada "tutucu" modelinin bir örneğini bulabilirsiniz. Ayrıca, çift kontrol edilen uçucu yaklaşımı gösteren gerçek dünyadan bir örnek var. Bu yardımcı olur umarım.

I wrote an article about this recently. Deconstructing The Singleton . You can find more info on these examples and an example of the "holder" pattern there. There is also a real-world example showcasing the double-checked volatile approach. Hope this helps.

6
Michael Andrews

Basit bir singleton uygulaması nasıl yapılır:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Düzgün tembel nasıl singleton oluşturmak için budur:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}
4
Nicolas Filotto

Bir sınıfın örnek değişkenini tembel olarak yüklemeniz gerekirse, double-control deyimi gerekir. Eğer statik olarak bir statik değişken veya bir singleton yüklemeniz gerekiyorsa, talep sahibine initilizasyon deyimine ihtiyacınız vardır. 

Ayrıca, eğer singleton'un sakin olması gerekiyorsa, diğer tüm alanların geçici olması ve singleton nesnesinin değişmez kalmasını sağlamak için readResolve () yönteminin uygulanması gerekir. Aksi takdirde, nesne her seri hale getirildiğinde, nesnenin yeni bir örneği oluşturulur. ReadResolve () işlevinin yaptığı, readObject () tarafından okunan yeni nesneyi değiştirmektir; bu, yeni nesnenin kendisine atıfta bulunulan hiçbir değişken olmadığı için toplanması için zorlanır.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 
3
Onur

Singleton nesnesini yapmanın çeşitli yolları:

  1. Joshua Bloch'a göre - Enum en iyisi olur.

  2. ayrıca çift kontrol kilidini de kullanabilirsiniz.

  3. İç statik sınıf bile kullanılabilir.

3
Shailendra Singh

Enum singleton

İplik emniyeti olan bir Singleton uygulamanın en basit yolu, bir Enum kullanmaktır.

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

Bu kod, Java 1.5’te Enum’un kullanılmasından bu yana

Çift kontrollü kilitleme

Çok iş parçacıklı bir ortamda (Java 1.5'ten başlayarak) çalışan bir “klasik” singletonu kodlamak istiyorsanız, bunu kullanmalısınız.

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class){
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance ;
  }
}

Bu, 1.5'ten önce iş parçacığı için güvenli değildir, çünkü geçici anahtar kelimenin uygulanması farklıydı.

Erken yükleme Singleton (Java 1.5'ten önce bile çalışır)

Bu uygulama, sınıf yüklendiğinde singleton'u başlatır ve iplik güvenliği sağlar.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}
3
Dan Moldovan

JSE 5.0 ve üstü için, Enum yaklaşımını kullanın, aksi halde statik tekil tutucu tutucu yaklaşımı kullanın ((Bill Pugh tarafından tarif edilen tembel bir yükleme yaklaşımı).

2
raoadnan

Singletons'a karşı sıklıkla kullanılan diğer bir argüman, test edilebilirlik problemleridir. Singletons, test amacıyla kolayca takılabilir değildir. Bu bir sorun olarak ortaya çıkarsa, aşağıdaki ufak tefek değişiklikleri yapmak isterim:

public class SingletonImpl {

    private static SingletonImpl instance;

    public static SingletonImpl getInstance() {
        if (instance == null) {
            instance = new SingletonImpl();
        }
        return instance;
    }

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

Eklenen setInstance yöntemi, test sırasında singleton sınıfının mockup uygulamasının ayarlanmasına izin verir:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Bu aynı zamanda erken başlangıç ​​yaklaşımlarıyla da çalışır:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Bu, bu işlevselliği normal uygulamaya da gösterme dezavantajına sahiptir. Bu kod üzerinde çalışan diğer geliştiriciler, belirli bir işlevi değiştirmek için 'setInstance' yöntemini kullanmaya ve bu nedenle tüm uygulama davranışını değiştirmek için cazip olabilir, bu nedenle bu yöntem en azından javadoc'ta iyi bir uyarı içermelidir.

Yine de, mockup testi imkanı (gerektiğinde) için bu koda maruz kalma, ödenmesi gereken makul bir fiyat olabilir.

2
user3792852

en basit singleton sınıfı

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
1
rohan kamat

Java 1.5'ten sonra, enum'un mevcut olan en iyi singleton uygulaması olduğunu düşünüyorum çünkü çok dişli ortamlarda bile - yalnızca bir örnek oluşturulmasını sağlıyor.

public enum Singleton{ INSTANCE; }

ve bitti !!!

0
shikjohari

Bu yazıya bir göz atın.

Java'nın çekirdek kütüphanelerindeki GoF Design Patterns örnekleri

En iyi cevap veren "Singleton" bölümünden,

Singleton (her seferinde aynı örneği (genellikle kendisinin) döndüren yaratıcı yöntemlerle tanınır)

  • Java.lang.Runtime # getRuntime ()
  • Java.awt.Desktop # getDesktop ()
  • Java.lang.System # getSecurityManager ()

Ayrıca Singleton örneğini Java yerel sınıflarından da öğrenebilirsiniz.

0
phi

Gördüğüm en iyi singleton modeli Tedarikçi arayüzünü kullanıyor.

  • Genel ve tekrar kullanılabilir
  • Tembel başlatmayı destekler
  • Sadece başlatılana kadar senkronize edilir, ardından engelleme yapan tedarikçi, engelleme yapmayan bir tedarikçi ile değiştirilir.

Aşağıya bakınız:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}
0
user1024314