Jaki jest skuteczny sposób implementacji wzorca Singletona w Javie? [zamknięte]

Jaki jest skuteczny sposób implementacji wzorca Singletona w Javie?

Author: Erick Robertson, 2008-09-16

30 answers

Użyj enum:

public enum Foo {
    INSTANCE;
}

Joshua Bloch wyjaśnił to podejście w swoimEffective Java Reloaded przemówieniu na Google I/O 2008: link do wideo . Zobacz także slajdy 30-32 jego prezentacji (effective_java_reloaded.pdf):

Właściwy sposób implementacji Serializowalnego Singletona

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

Edit: An Online part of "Effective Java" says:

" takie podejście jest funkcjonalnie równoważne społeczeństwu podejście polowe, z tym że jest bardziej zwięzłe, zapewnia maszynerię serializacji za darmo i zapewnia żelazną gwarancję przed wieloma instancjami, nawet w obliczu wyrafinowanych ataków serializacji lub odbicia. Chociaż podejście to nie zostało jeszcze szeroko przyjęte, Jednoelementowy typ enum jest najlepszym sposobem implementacji singleton."

 726
Author: Stephen Denne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-15 09:23:02

W zależności od zastosowania, istnieje kilka "poprawnych" odpowiedzi.

Od java5 najlepszym sposobem na to jest użycie enum:

public enum Foo {
   INSTANCE;
}

Pre java5, najprostszym przypadkiem jest:

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");
    }
}
Przejdźmy do kodu. Po pierwsze, chcesz, żeby klasa była ostateczna. W tym przypadku użyłem słowa kluczowego final, aby poinformować użytkowników, że jest ostateczna. Następnie musisz uczynić konstruktor prywatnym, aby uniemożliwić użytkownikom tworzenie własnych Foo. Wyrzucenie wyjątku z konstruktora uniemożliwia użytkownikom aby użyć odbicia do stworzenia drugiego Foo. Następnie tworzy się pole private static final Foo do przechowywania jedynej instancji i metodę public static Foo getInstance() do zwracania jej. Specyfikacja Java zapewnia, że konstruktor jest wywoływany tylko wtedy, gdy klasa jest używana po raz pierwszy.

Jeśli masz bardzo duży obiekt lub ciężki kod konstrukcyjny, a także inne dostępne statyczne metody lub pola, które mogą być używane przed potrzebną instancją, wtedy i tylko wtedy musisz użyć leniwej inicjalizacji.

Możesz użyć private static class aby załadować instancję. Kod będzie wtedy wyglądał następująco:

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

Ponieważ linia private static final Foo INSTANCE = new Foo(); jest wykonywana tylko wtedy, gdy Klasa FooLoader jest faktycznie użyta, to zajmuje się leniwą instancją i jest gwarantowana, że będzie bezpieczna dla wątku.

Jeśli chcesz również mieć możliwość serializacji obiektu, musisz upewnić się, że deserializacja nie utworzy kopii.

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

Metoda readResolve() upewni się, że zostanie zwrócona jedyna instancja, nawet gdy obiekt był serializowany w poprzednim uruchomieniu programu.

 219
Author: Roel Spilker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-05 08:39:13

Zastrzeżenie: właśnie podsumowałem wszystkie niesamowite odpowiedzi i napisałem je moimi słowami.


Podczas implementacji Singletona mamy 2 opcje
1. Lazy loading
2. Wczesne Ładowanie

Leniwe Ładowanie dodaje narzut bitowy (szczerze mówiąc dużo), więc używaj go tylko wtedy, gdy masz bardzo duży obiekt lub ciężki kod konstrukcyjny, a także inne dostępne statyczne metody lub pola, które mogą być używane przed potrzebą wystąpienia, wtedy i tylko wtedy musisz użyj leniwej inicjalizacji.W przeciwnym razie wybór wczesnego ładowania jest dobrym wyborem.

Najprostszym sposobem implementacji Singletona jest

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

Wszystko jest dobre, z wyjątkiem wcześniej załadowanego Singletona. Lets try lazy loaded singleton

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

Jak na razie tak dobrze, ale nasz bohater nie przeżyje walcząc samotnie z wieloma złymi wątkami, które chcą wielu wielu instancji naszego bohatera. Więc pozwala chronić go przed złem wielowątkowość

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

Ale to nie wystarczy, aby chronić się bohater, naprawdę!!! To jest najlepsze, co możemy/powinniśmy zrobić, aby pomóc naszemu bohaterowi

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

Nazywa się to "idiom blokowania Podwójnie sprawdzonego". Łatwo zapomnieć o ulotnym oświadczeniu i trudno zrozumieć, dlaczego jest to konieczne.
Szczegóły: http://www.cs.umd.edu / ~pugh/java/memoryModel/DoubleCheckedLocking.html

Teraz jesteśmy pewni co do złego wątku, ale co z okrutną serializacją? Musimy się upewnić, że nawet podczas de-serializacji żaden nowy obiekt nie jest created

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

Metoda readResolve() upewni się, że zostanie zwrócona jedyna instancja, nawet jeśli obiekt był serializowany w poprzednim uruchomieniu naszego programu.

Wreszcie dodaliśmy wystarczającą ochronę przed wątkami i serializacją, ale nasz kod wygląda nieporęcznie i brzydko. Lets give our hero a make over

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

Tak to jest nasz sam bohater :)
Ponieważ linia private static final Foo INSTANCE = new Foo(); jest wykonywana tylko wtedy, gdy klasa FooLoader jest rzeczywiście używana, to zajmuje się leniwymi instancjacja,

I czy gwarantowane jest bezpieczeństwo wątku.

I zaszliśmy tak daleko, oto najlepszy sposób, aby osiągnąć wszystko, co zrobiliśmy, jest najlepszy możliwy sposób

 public enum Foo {
       INSTANCE;
   }

Które wewnętrznie będą traktowane jak

public class Foo {

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

To koniec strachu przed serializacją, wątkami i brzydkim kodem. Również liczby singleton są leniwie inicjowane.

To podejście jest funkcjonalnie równoważne z podejściem w terenie publicznym, z wyjątkiem że jest bardziej zwięzły, zapewnia Maszyny serializacyjne za darmo i zapewnia żelazną gwarancję przed wieloma instancji, nawet w obliczu skomplikowanej serializacji lub ataki odbicia. Choć podejście to nie zostało jeszcze szeroko przyjęte, Jednoelementowy typ enum jest najlepszym sposobem implementacji Singletona.

-Joshua Bloch w "efektywnej Javie"

Teraz możesz zdać sobie sprawę, dlaczego ENUMS są uważane za najlepszy sposób na wdrożenie Singleton i dzięki za cierpliwość:)
Updated it on my blog.

 121
Author: xyz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:37

Rozwiązanie opublikowane przez Stu Thompsona jest ważne w Java5.0 i późniejszych. Ale wolałbym go nie używać, ponieważ myślę, że jest podatny na błędy.

Łatwo zapomnieć ulotne stwierdzenie i trudno zrozumieć, dlaczego jest to konieczne. Bez lotności kod ten nie byłby już bezpieczny ze względu na podwójnie sprawdzoną blokadę antypattern. Zobacz więcej na ten temat w paragrafie 16.2.4 współbieżność Javy w praktyce . W skrócie: ten wzorzec (przed Java5. 0 lub bez volatile statement) może zwrócić odniesienie do obiektu Bar, który jest (nadal) w nieprawidłowym stanie.

Ten wzór został wymyślony w celu optymalizacji wydajności. Ale to już nie jest prawdziwy problem. Poniższy leniwy kod inicjalizacyjny jest szybki i - co ważniejsze-łatwiejszy do odczytania.

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

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
 120
Author: Benno Richters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-06 16:40:30

Thread safe in Java 5+:

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 : zwróć uwagę na modyfikator volatile tutaj. :) Jest to ważne, ponieważ bez niego inne wątki nie są gwarantowane przez JMM (Java Memory Model), aby zobaczyć zmiany jego wartości. Synchronizacja nie zajmuje się tym--jedynie serializuje dostęp do tego bloku kodu.

Edycja 2 : @Bno ' s answer details the approach recommended by Bill Pugh (FindBugs) and is arguable better. Czytaj i głosuj jego odpowiedź też.

 93
Author: Stu Thompson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-14 13:48:09

Zapomnij leniwa inicjalizacja , to zbyt problematyczne. Jest to najprostsze rozwiązanie:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
 87
Author: Jonathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 15:01:50

Upewnij się, że naprawdę tego potrzebujesz. Zrób google dla "singleton anty-pattern", aby zobaczyć kilka argumentów przeciwko. Przypuszczam, że nie ma w tym nic złego z natury, ale jest to tylko mechanizm ujawniania globalnych zasobów / danych, więc upewnij się, że jest to najlepszy sposób. W szczególności znalazłem dependency injection bardziej przydatne szczególnie, jeśli używasz również testów jednostkowych, ponieważ DI pozwala na korzystanie z wyśmiewanych zasobów do celów testowych.

 45
Author: Neil Burroughs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-11 08:27:32

Nie zapominaj, że Singleton jest tylko Singletonem dla Classloadera, który go załadował. Jeśli używasz wielu ładowarek (kontenerów), każdy może mieć własną wersję Singletona.

 25
Author: Javamann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 18:12:31

Jestem zdumiony niektórymi odpowiedziami, które sugerują DI jako alternatywę dla używania singletonów; są to niezwiązane pojęcia. Za pomocą DI można wstrzykiwać instancje singleton lub inne niż singleton (np. na wątek). Przynajmniej jest to prawdą, jeśli używasz Spring 2.x, nie mogę się wypowiadać za inne frameworki.

Więc moja odpowiedź na OP byłaby (we wszystkich, ale najbardziej trywialny przykładowy kod) do:

  1. Użyj frameworka DI jak Spring, wtedy
  2. Make it part of your di configuration niezależnie od tego, czy Twoje zależności To singletony, zakres żądania, zakres sesji, czy cokolwiek innego.

To podejście daje Ci ładną, odsprzęgniętą (a więc elastyczną i testowalną) architekturę, w której to, czy używać Singletona, jest łatwo odwracalnym szczegółem implementacji(pod warunkiem, że wszystkie singletony, których używasz, są oczywiście bezpieczne dla wątków).

 20
Author: Andrew Swan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 12:06:04

Zastanów się, dlaczego potrzebujesz Singletona, zanim go napiszesz. Istnieje quasi-Religijna debata na temat ich używania, którą można dość łatwo potknąć, jeśli wygooglujesz singletony w Javie.

Osobiście staram się unikać singletonów tak często, jak to możliwe z wielu powodów, z których większość można znaleźć w googlach singletonów. Czuję, że dość często singletony są nadużywane, ponieważ są łatwe do zrozumienia przez wszystkich, są wykorzystywane jako mechanizm pobierania "globalnych" danych do OO design i są one używane, ponieważ łatwo jest obejść zarządzanie cyklem życia obiektu (lub naprawdę myśleć o tym, jak można zrobić A od wewnątrz B). Spójrz na rzeczy takie jak inwersja sterowania (IoC) lub iniekcji zależności (DI)dla ładnego środka.

Jeśli naprawdę go potrzebujesz, wikipedia ma dobry przykład właściwej implementacji singletonu.

 19
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 09:48:54

Poniżej przedstawiono 3 różne podejścia

1) Enum

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

2) Podwójnie sprawdzane blokowanie / leniwe ładowanie

/**
* 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) statyczna metoda fabryczna

/**
* 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;
    }
}
 15
Author: Abhijit Gaikwad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-17 03:46:40

Używam Spring Framework do zarządzania singletonami. Nie wymusza "singleton-ness" klasy (czego tak naprawdę nie możesz zrobić, jeśli w grę wchodzi wiele klas loaderów), ale zapewnia naprawdę łatwy sposób na budowanie i konfigurowanie różnych fabryk do tworzenia różnych typów obiektów.

 12
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 20:44:55

Wersja 1:

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

Lazy loading, thread safe with blocking, low performance because of synchronized.

Wersja 2:

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

Leniwe Ładowanie, bezpieczne nici z nieblokowaniem, wysoka wydajność.

 11
Author: coderz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-09 04:08:16

Wikipedia ma kilka przykładów singletonów, również w Javie. Implementacja Java 5 wygląda na całkiem kompletną i jest bezpieczna dla wątków (zastosowano podwójnie sprawdzoną blokadę).

 10
Author: macbirdie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 09:35:42

Jeśli nie potrzebujesz leniwego ładowania, po prostu spróbuj

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

Jeśli chcesz leniwe ładowanie i chcesz, aby Singleton był bezpieczny dla wątków, spróbuj podwójnego sprawdzania wzorca

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

Ponieważ schemat podwójnego sprawdzania nie jest gwarantowany (z powodu jakiegoś problemu z kompilatorami, Nie wiem nic więcej na ten temat.), możesz również spróbować zsynchronizować całą metodę getInstance lub utworzyć rejestr dla wszystkich singletonów.

 10
Author: Aleksi Yrttiaho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 04:36:50

Powiedziałbym Enum singleton

Singleton używanie enum w Javie jest ogólnie sposobem deklarowania enum singleton. Enum singleton może zawierać zmienną instancji i metodę instancji. Dla uproszczenia, zauważ również, że jeśli używasz dowolnej metody instancji, musisz zapewnić bezpieczeństwo wątku tej metody, jeśli w ogóle wpływa ona na stan obiektu.

Użycie enum jest bardzo łatwe do zaimplementowania i nie ma wad dotyczących obiektów serializowalnych, które należy obejść w inny sposób.

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

Możesz uzyskać do niego dostęp za pomocą Singleton.INSTANCE, znacznie łatwiej niż wywołanie metody getInstance() w Singletonie.

1.12 serializacja stałych Enum

Stałe Enum są serializowane inaczej niż zwykłe obiekty serializowalne lub zewnętrznie. Serializowana forma stałej enum składa się wyłącznie z jej nazwy; wartości pól stałej nie są obecne w postaci. Aby serializować stałą enum, ObjectOutputStream zapisuje wartość zwracaną przez enum metoda imienia Constanta. Aby deserializować stałą enum, ObjectInputStream odczytuje nazwę stałej ze strumienia; deserializowana stała jest następnie uzyskiwana przez wywołanie metody java.lang.Enum.valueOf, przekazując Typ enum stałej wraz z otrzymaną nazwą stałej jako argumenty. Podobnie jak inne obiekty serializowalne lub zewnętrzne, stałe enum mogą działać jako cele odwołań wstecznych pojawiających się następnie w strumieniu serializacji.

Proces, w którym stałe enum są serializowane nie można dostosować: każda klasa specyficzna writeObject, readObject, readObjectNoData, writeReplace, i readResolve metody zdefiniowane przez typy enum są ignorowane podczas serializacji i deserializacji. Podobnie, wszelkie deklaracje pól serialPersistentFields lub serialVersionUID są również ignorowane-wszystkie typy enum mają stałą serialVersionUID z 0L. Dokumentowanie pól serializowalnych i danych dla typów enum jest niepotrzebne, ponieważ nie ma różnic w typie przesyłanych danych.

Cytaty z Oracle docs

Kolejny problem z konwencjonalnymi Singletonami jest to, że po zaimplementowaniu interfejsu Serializable, nie pozostają one już Singletonem, ponieważ metoda readObject() zawsze zwraca nową instancję, taką jak constructor w Javie. Można tego uniknąć, używając readResolve() i odrzucając nowo utworzoną instancję, zastępując ją singletonem, jak poniżej

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

Może to stać się jeszcze bardziej złożone, jeśli Klasa Singleton zachowuje stan, ponieważ musisz uczynić je przejściowymi, ale z in Enum Singleton serializacja jest gwarantowana przez JVM.


Dobra Lektura

  1. Wzór Singletona
  2. Enums, Singletons and Deserialisation
  3. Zamek Podwójnie sprawdzony i wzór Singletona
 8
Author: NullPoiиteя,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:02
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");
    }
}
 6
Author: Dheeraj Sachan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-09 13:25:25

Może trochę za późno na grę, ale jest wiele niuansów wokół implementacji Singletona. Wzór uchwytu nie może być stosowany w wielu sytuacjach. IMO przy używaniu lotnej-należy również użyć zmiennej lokalnej. Zacznijmy od początku i powtórzmy problem. Zobaczysz, co mam na myśli.


Pierwsza próba może wyglądać mniej więcej tak:

public class MySingleton {

     private static MySingleton INSTANCE;

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

        return INSTANCE;
    }
    ...
}

Tutaj mamy klasę MySingleton, która ma prywatny statyczny członek o nazwie INSTANCE, oraz publiczna statyczna metoda getInstance(). Przy pierwszym wywołaniu getInstance() składnikiem instancji jest null. Następnie przepływ przejdzie do stanu tworzenia i utworzy nową instancję klasy MySingleton. Kolejne wywołania getInstance() stwierdzą, że zmienna instancji jest już ustawiona i dlatego nie tworzą kolejnej instancji MySingleton. Zapewnia to, że istnieje tylko jedna instancja MySingleton, która jest współdzielona przez wszystkie wywołania getInstance ().

Ale to implementacja ma problem. Aplikacje wielowątkowe będą miały warunek rasy przy tworzeniu pojedynczej instancji. Jeśli wiele wątków wykonania uderzy w metodę getInstance () w (lub w pobliżu) tym samym czasie, każdy z nich zobaczy element instancji jako null. Spowoduje to, że każdy wątek utworzy nową instancję MySingleton, a następnie ustawi skład instancji.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Tutaj użyliśmy słowa kluczowego zsynchronizowanego w sygnaturze metody, aby zsynchronizować metoda getInstance (). To z pewnością poprawi naszą kondycję rasową. Wątki będą teraz blokować i wprowadzać metodę pojedynczo. Ale stwarza to również problem wydajności. Ta implementacja nie tylko synchronizuje tworzenie pojedynczej instancji, ale także synchronizuje wszystkie wywołania getInstance (), w tym odczyty. Odczyty nie muszą być synchronizowane, ponieważ po prostu zwracają wartość instancji. Ponieważ odczyty będą stanowiły większość naszych połączeń (pamiętaj, instancja dzieje się tylko przy pierwszym połączeniu), poniesiemy niepotrzebny hit wydajności poprzez synchronizację całej metody.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Tutaj przenieśliśmy synchronizację z sygnatury metody do zsynchronizowanego bloku, który zawija tworzenie instancji MySingleton. Ale czy to rozwiązuje nasz problem? Cóż, nie blokujemy już czytań, ale zrobiliśmy również krok w tył. Wiele wątków trafi w metodę getInstance () lub mniej więcej w tym samym czasie i wszystkie będą widzieć element instancji jako null. Będą wtedy naciśnij zsynchronizowany blok, w którym uzyskasz blokadę i utworzysz instancję. Gdy wątek opuści blok, pozostałe wątki będą walczyć o blokadę, a jeden po drugim każdy wątek spadnie przez blok i utworzy nową instancję naszej klasy. Wracamy do punktu wyjścia.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Tutaj wystawiamy kolejny czek z wnętrza bloku. Jeśli element instancji został już ustawiony, pominiemy inicjalizację. Nazywa się to podwójnie sprawdzonym blokowaniem.

To rozwiązuje nasz problem z wieloma instancjami. Ale po raz kolejny nasze rozwiązanie stanowiło kolejne wyzwanie. Inne wątki mogą nie "widzieć", że członek instancji został zaktualizowany. Wynika to z tego, jak Java optymalizuje operacje na pamięci. Wątki kopiują oryginalne wartości zmiennych z pamięci głównej do pamięci podręcznej procesora. Zmiany wartości są następnie zapisywane do tego bufora i odczytywane z niego. Jest to funkcja Java zaprojektowana w celu optymalizacji wydajności. Ale to stwarza problem dla naszego Singletona wdrożenie. Drugi wątek - przetwarzany przez inny procesor lub rdzeń, przy użyciu innej pamięci podręcznej - nie zobaczy zmian wprowadzonych przez pierwszy. Spowoduje to, że drugi wątek zobaczy instancję Członka jako null, zmuszając do utworzenia nowej instancji naszego Singletona.


private static volatile MySingleton INSTANCE;

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

    return INSTANCE;
}

Rozwiązujemy to używając słowa kluczowego volatile na Deklaracji członka instancji. To powie kompilatorowi, aby zawsze odczytywał i zapisywał z pamięci głównej, a nie z pamięci podręcznej procesora.

Ale ta prosta zmiana ma swoją cenę. Ponieważ omijamy pamięć podręczną procesora, za każdym razem, gdy operujemy na członie instancji volatile, będziemy mieć wpływ na wydajność - co robimy 4 razy. Dwukrotnie sprawdzamy istnienie (1 i 2), ustawiamy wartość (3) , a następnie zwracamy wartość (4). Można argumentować, że ta ścieżka jest przypadkiem marginalnym, ponieważ instancję tworzymy tylko podczas pierwszego wywołania metody. Być może uderzenie performatywne w kreację jest znośne. Ale nawet nasz główny przypadek użycia, czyta, będzie działał na członek Lotny dwukrotnie. Raz sprawdzić istnienie i ponownie zwrócić jego wartość.


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

Ponieważ wydajność jest spowodowana działaniem bezpośrednio na członie lotnym, ustawmy zmienną lokalną na wartość lotną i zamiast tego operujmy na zmiennej lokalnej. Zmniejszy to liczbę operacji na volatile, odzyskując w ten sposób część utraconej wydajności. Zauważ, że musimy ponownie ustawić naszą zmienną lokalną, gdy wejdziemy do zsynchronizowanego bloku. Zapewnia to jest na bieżąco ze wszystkimi zmianami, które zaszły podczas czekania na zamek.

Napisałem niedawno artykuł na ten temat. Dekonstruowanie Singletonu . Możesz znaleźć więcej informacji na temat tych przykładów i przykład wzoru "posiadacza". Istnieje również prawdziwy przykład pokazujący podwójnie sprawdzone podejście lotne. Mam nadzieję, że to pomoże.

 4
Author: Michael Andrews,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-12 16:06:06

Potrzebujesz idiomu podwójnego sprawdzenia , Jeśli chcesz leniwie załadować zmienną instancji klasy. Jeśli chcesz ładować zmienną statyczną lub singleton leniwie, potrzebujesz idiomu initilization on demand holder .

Ponadto, jeśli singleton musi być seriliazble, wszystkie inne pola muszą być transient i musi być zaimplementowana metoda readResolve (), aby utrzymać niezmienny obiekt singleton. W przeciwnym razie za każdym razem, gdy obiekt jest deserializowany, nowy instancja obiektu zostanie wytworzona. To, co robi readResolve (), zastępuje nowy obiekt odczytany przez readObject (), co wymusiło, że nowy obiekt jest zbierany jako śmieci, ponieważ nie ma żadnej zmiennej odnoszącej się do niego.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 
 3
Author: Onur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-02 20:49:18

Różne sposoby tworzenia obiektu singleton:

  1. Według Joshuy Bloch-Enum byłoby najlepsze.

  2. Możesz również użyć podwójnego zamka.

  3. Można użyć nawet wewnętrznej klasy statycznej.

 3
Author: Shailendra Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-29 12:42:30

Enum singleton

Najprostszym sposobem implementacji Singletona, który jest bezpieczny dla wątków, jest użycie Enum

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

Ten kod działa od wprowadzenia Enum w Javie 1.5

Blokada Podwójnie sprawdzona

Jeśli chcesz kodować" klasyczny " singleton, który działa w środowisku wielowątkowym (począwszy od Javy 1.5), powinieneś użyć tego.

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

Nie jest to bezpieczne dla wątków przed 1.5, ponieważ implementacja słowa kluczowego volatile była inaczej.

Wczesne Ładowanie Singletona (działa nawet przed Javą 1.5)

Ta implementacja tworzy instancje Singletona podczas ładowania klasy i zapewnia bezpieczeństwo wątku.

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
Author: Dan Moldovan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-11 17:58:05

Oto jak zaimplementować prostą singleton:

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

Oto jak prawidłowo stworzyć swój singleton:

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();
    }
}
 3
Author: Nicolas Filotto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-29 18:50:11

Dla JSE 5.0 i nowszych zastosuj podejście Enum, w przeciwnym razie użyj podejścia static singleton holder ((podejście leniwe Ładowanie opisane przez Billa Pugha). To ostatnie rozwiązanie jest również bezpieczne dla wątków bez konieczności stosowania specjalnych konstrukcji językowych (np. lotnych lub synchronicznych).

 2
Author: raoadnan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-22 18:51:33

Innym argumentem często stosowanym przeciwko Singletonom są ich problemy z testowalnością. Singletony nie są łatwe do wyśmiewania w celach testowych. Jeśli okaże się to problemem, Lubię dokonać następujących drobnych modyfikacji:

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

Dodana Metoda setInstance umożliwia ustawienie makiety implementacji klasy singleton podczas testowania:

public class SingletonMock extends SingletonImpl {

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

}

Działa to również z podejściami wczesnej inicjalizacji:

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

}

Ma to wadę eksponowania tej funkcjonalności na normalne zastosowanie też. Inni programiści pracujący nad tym kodem mogą pokusić się o użycie metody setInstance do zmiany konkretnej funkcji, a tym samym zmiany całego zachowania aplikacji, dlatego ta metoda powinna zawierać przynajmniej dobre ostrzeżenie w swoim javadoc.

Mimo to, dla możliwości testowania makiet (w razie potrzeby), ekspozycja kodu może być akceptowalną ceną do zapłacenia.

 2
Author: user3792852,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-14 12:08:22

Najprostsza Klasa Singletona

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
 0
Author: rohan kamat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-14 13:44:14

Nadal uważam, że po Javie 1.5 enum jest najlepszą dostępną implementacją Singletona, ponieważ zapewnia również, że nawet w środowiskach wielowątkowych - tworzona jest tylko jedna instancja.

public enum Singleton{ INSTANCE; }

I jesteś skończony !!!
 0
Author: shikjohari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-06 07:22:59

Spójrz na ten post.

Przykłady wzorców projektowych GoF w podstawowych bibliotekach Javy

Z sekcji "Singleton" najlepszej odpowiedzi,

W przeciwieństwie do innych języków, w których nie ma żadnego języka, nie ma żadnego języka.]}
  • java.lang.Runtime#getRuntime ()
  • java.awt.Desktop # getDesktop ()
  • java.lang.System # getSecurityManager ()

Możesz także nauczyć się przykład Singletona z samych klas Java native.

 0
Author: phi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:27

Najlepszy wzór Singletona, jaki widziałem, używa interfejsu dostawcy.

    Jest uniwersalny i wielokrotnego użytku]}
  • obsługuje leniwą inicjalizację
  • jest synchronizowany tylko do momentu zainicjowania, a następnie dostawca blokujący zostaje zastąpiony dostawcą nieblokującym.

Patrz poniżej:

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
Author: Robert Thornton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-10 21:05:02

Czasami proste "static Foo foo = new Foo();" to za mało. Po prostu pomyśl o podstawowym wstawianiu danych, które chcesz zrobić.

Z drugiej strony będziesz musiał zsynchronizować każdą metodę, która tworzy instancję zmiennej singleton jako takiej. Synchronizacja nie jest zła jako taka, ale może prowadzić do problemów z wydajnością lub blokowania (w bardzo rzadkich sytuacjach, używając tego przykładu. Rozwiązaniem jest

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}
Co teraz? Klasa jest ładowana przez class loader. Bezpośrednio po zajęciach był maszyna wirtualna, interpretowana z tablicy bajtów, wykonuje static { } - block. w tym tkwi cała tajemnica: blok statyczny jest wywoływany tylko raz, kiedy dana klasa (nazwa) danego pakietu jest ładowana przez tą jedną klasę loader.
 -3
Author: Georgi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-26 06:03:05
public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
    if (INSTANCE != null)
        throw new IllegalStateException (“Already instantiated...”);
}

    public synchronized static Singleton getInstance() { 
    return INSTANCE;

    }

}

Ponieważ dodaliśmy zsynchronizowane słowo kluczowe przed getInstance, uniknęliśmy warunku wyścigu w przypadku, gdy dwa wątki wywołują getInstance w tym samym czasie.

 -5
Author: somenath mukhopadhyay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-14 13:44:05