CodeGym/Blog Java/Aleatoriu/Clasa Java Singleton
John Squirrels
Nivel
San Francisco

Clasa Java Singleton

Publicat în grup
Bună! Astăzi ne vom scufunda în detaliile diferitelor modele de design, începând cu modelul Java Singleton. Să trecem în revistă: ce știm despre modelele de design în general? Modelele de design sunt cele mai bune practici pe care le putem aplica pentru a rezolva o serie de probleme cunoscute. Modelele de design nu sunt, în general, legate de niciun limbaj de programare. Gândiți-vă la ele ca la un set de recomandări care să vă ajute să evitați greșelile și să evitați reinventarea roții.Modele de design: Singleton - 1

Ce este un singleton în Java?

Singleton este unul dintre cele mai simple modele de design la nivel de clasă. Uneori oamenii spun „această clasă este singleton”, ceea ce înseamnă că clasa implementează modelul de design singleton. Uneori este necesar să scriem o clasă în care limităm instanțiarea la un singur obiect. De exemplu, o clasă responsabilă pentru înregistrarea sau conectarea la un baza de date. Modelul de design singleton descrie cum putem realiza acest lucru. Un model singleton este un model de design care face două lucruri:
  1. Acesta garantează că va exista o singură instanță a clasei.

  2. Oferă un singur punct de acces global la acea instanță.

Prin urmare, există două caracteristici care sunt caracteristice pentru aproape fiecare implementare a modelului singleton:
  1. Un constructor privat. Acest lucru limitează capacitatea de a crea obiecte ale clasei în afara clasei în sine.

  2. O metodă publică statică care returnează instanța clasei. Această metodă se numește getInstance . Acesta este punctul de acces global la instanța clasei.

Opțiuni de implementare

Modelul de design singleton este aplicat în diferite moduri. Fiecare opțiune este bună și rea în felul ei. Ca întotdeauna, nu există o opțiune perfectă aici, dar ar trebui să ne străduim pentru una. În primul rând, să decidem ce este bun și rău și ce măsurători afectează modul în care evaluăm diferitele implementări ale modelului de proiectare. Să începem cu binele. Iată factorii care fac o implementare mai suculentă și mai atrăgătoare:
  • Inițializare leneșă: instanța nu este creată până când este necesară.

  • Cod simplu și transparent: această metrică, desigur, este subiectivă, dar este importantă.

  • Siguranța firelor: funcționare corectă într-un mediu cu mai multe fire.

  • Performanță ridicată într-un mediu cu mai multe fire de execuție: blocarea firelor de execuție este mică sau deloc atunci când partajați o resursă.

Acum contra. Vom enumera factorii care pun o implementare într-o lumină proastă:
  • Fără inițializare leneșă: când se încarcă clasa când pornește aplicația, indiferent dacă este sau nu necesară (paradoxal, în lumea IT este mai bine să fii leneș)

  • Cod complex și greu de citit. Această măsurătoare este, de asemenea, subiectivă. Dacă ochii tăi încep să sângereze, vom presupune că implementarea nu este cea mai bună.

  • Lipsa siguranței firului. Cu alte cuvinte, „pericol de fir”. Funcționare incorectă într-un mediu cu mai multe fire.

  • Performanță slabă într-un mediu cu mai multe fire de execuție: firele de execuție se blochează reciproc tot timpul sau des atunci când partajează o resursă.

Cod

Acum suntem gata să luăm în considerare diverse opțiuni de implementare și să indicăm argumentele pro și contra:

Simplu

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

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
Cea mai simplă implementare. Pro:
  • Cod simplu și transparent

  • Siguranța firului

  • Performanță ridicată într-un mediu cu mai multe fire

Contra:
  • Fără inițializare leneșă.
În încercarea de a remedia deficiența anterioară, obținem implementarea numărul doi:

Inițializare leneșă

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Pro:
  • Inițializare leneșă.

Contra:
  • Nu este sigur pentru fire

Această implementare este interesantă. Putem inițializa leneș, dar am pierdut siguranța firelor. Nu vă faceți griji - sincronizăm totul în implementarea numărul trei.

Acces sincronizat

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Pro:
  • Inițializare leneșă.

  • Siguranța firului

Contra:
  • Performanță slabă cu mai multe fire

Excelent! În implementarea numărul trei, restabilim siguranța firelor! Desigur, este lent... Acum metoda getInstance este sincronizată, deci poate fi executată de un singur thread la un moment dat. În loc să sincronizăm întreaga metodă, trebuie de fapt doar să sincronizăm partea din ea care inițializează noua instanță. Dar nu putem folosi pur și simplu un bloc sincronizat pentru a încheia partea responsabilă cu crearea noii instanțe. Făcând asta nu ar asigura siguranța firului. Totul este un pic mai complicat. Sincronizarea corectă poate fi văzută mai jos:

Încuiere dublu verificată

public class Singleton {
    private static final Singleton INSTANCE;

  private Singleton() {
  }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
Pro:
  • Inițializare leneșă.

  • Siguranța firului

  • Performanță ridicată într-un mediu cu mai multe fire

Contra:
  • Nu este acceptat în versiunile anterioare de Java sub 1.5 (utilizarea cuvântului cheie volatil este fixată de la versiunea 1.5)

Rețineți că pentru ca această opțiune de implementare să funcționeze corect, trebuie îndeplinită una dintre cele două condiții. Variabila INSTANCE trebuie să fie finală sau volatilă . Ultima implementare pe care o vom discuta astăzi este clasa titular singleton .

Titularul clasei

public class Singleton {

   private Singleton() {
   }

   private static class SingletonHolder {
       public static final Singleton HOLDER_INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
       return SingletonHolder.HOLDER_INSTANCE;
   }
}
Pro:
  • Inițializare leneșă.

  • Siguranța firului.

  • Performanță ridicată într-un mediu cu mai multe fire.

Contra:
  • Funcționarea corectă necesită o garanție că obiectul singleton este inițializat fără erori. În caz contrar, primul apel la metoda getInstance va avea ca rezultat un ExceptionInInitializerError , iar toate apelurile ulterioare vor produce un NoClassDefFoundError .

Această implementare este aproape perfectă. Este leneș, sigur și rapid. Dar are o nuanță, așa cum se explică în lista de contra. Comparația diferitelor implementări ale modelului singleton:
Implementarea Inițializare leneșă Siguranța firului Performanță cu mai multe fire Când să folosiți?
Simplu - + Rapid Nu. Sau, eventual, atunci când inițializarea leneșă nu este importantă. Dar niciodată nu ar fi mai bine.
Inițializare leneșă + - Nu se aplică Întotdeauna când multithreading nu este necesar
Acces sincronizat + + Încet Nu. Sau, eventual, atunci când performanța multithreaded nu contează. Dar niciodată nu ar fi mai bine.
Încuiere dublu verificată + + Rapid În cazuri rare, când trebuie să gestionați excepții la crearea singleton-ului (când singletonul deținătorului clasei nu este aplicabil)
Titularul clasei + + Rapid Ori de câte ori este nevoie de multithreading și există garanția că obiectul singleton va fi creat fără probleme.

Avantaje și dezavantaje ale modelului singleton

În general, un singleton face exact ceea ce se așteaptă de la el:
  1. Acesta garantează că va exista o singură instanță a clasei.

  2. Oferă un singur punct de acces global la acea instanță.

Cu toate acestea, acest model are deficiențe:
  1. Un singleton încalcă principiul responsabilității unice: pe lângă sarcinile sale directe, clasa singleton controlează și numărul de instanțe.

  2. Dependența unei clase obișnuite de un singleton nu este vizibilă în contractul public al clasei.

  3. Variabilele globale sunt proaste. În cele din urmă, un singleton se transformă într-o variabilă globală puternică.

  4. Prezența unui singleton reduce capacitatea de testare a aplicației în ansamblu și a claselor care folosesc singletonul în special.

Si asta e! :) Am explorat Java Singleton Class cu tine. Acum, pentru tot restul vieții, când conversați cu prietenii programatori, puteți menționa nu numai cât de bun este modelul, ci și câteva cuvinte despre ceea ce îl face rău. Succes în stăpânirea acestor noi cunoștințe.

Lectură suplimentară:

Comentarii
  • Popular
  • Nou
  • Vechi
Trebuie să fii conectat pentru a lăsa un comentariu
Această pagină nu are încă niciun comentariu