Singletons in Java

1. Inleiding

In dit korte artikel bespreken we de twee meest populaire manieren om Singletons in gewoon Java te implementeren.

2. Op klassen gebaseerde Singleton

De meest populaire benadering is om een ​​Singleton te implementeren door een gewone klas te maken en ervoor te zorgen dat deze het volgende heeft:

  • Een particuliere constructeur
  • Een statisch veld dat zijn enige exemplaar bevat
  • Een statische fabrieksmethode voor het verkrijgen van de instantie

We zullen ook een info-eigenschap toevoegen, alleen voor later gebruik. Onze implementatie ziet er dus als volgt uit:

openbare laatste klasse ClassSingleton {privé statische ClassSingleton INSTANCE; private String info = "Initial info class"; private ClassSingleton () {} openbare statische ClassSingleton getInstance () {if (INSTANCE == null) {INSTANCE = nieuwe ClassSingleton (); } keer INSTANCE terug; } // getters en setters}

Hoewel dit een veel voorkomende benadering is, is het belangrijk op te merken dat dit kan problematisch zijn in multithreading-scenario's, wat de belangrijkste reden is om Singletons te gebruiken.

Simpel gezegd, het kan in meer dan één geval resulteren, waardoor het kernprincipe van het patroon wordt doorbroken. Hoewel er vergrendelingsoplossingen voor dit probleem zijn, lost onze volgende benadering deze problemen op rootniveau op.

3. Enum Singleton

Laten we voor de toekomst geen andere interessante benadering bespreken - namelijk het gebruik van opsommingen:

openbare opsomming EnumSingleton {INSTANCE ("Initial class info"); privé String-info; private EnumSingleton (String info) {this.info = info; } openbare EnumSingleton getInstance () {terugkeer INSTANCE; } // getters en setters}

Deze benadering heeft serialisering en thread-veiligheid gegarandeerd door de enum-implementatie zelf, die intern verzekert dat alleen de enkele instantie beschikbaar is, waardoor de problemen worden verholpen die worden opgemerkt in de op klassen gebaseerde implementatie.

4. Gebruik

Om onze ClassSingleton, we hoeven alleen de instantie statisch op te halen:

ClassSingleton classSingleton1 = ClassSingleton.getInstance (); System.out.println (classSingleton1.getInfo ()); // Initiële klasse-info ClassSingleton classSingleton2 = ClassSingleton.getInstance (); classSingleton2.setInfo ("Nieuwe klasse-info"); System.out.println (classSingleton1.getInfo ()); // Nieuwe klasse-info System.out.println (classSingleton2.getInfo ()); // Nieuwe lesinformatie

Wat betreft de EnumSingleton, we kunnen het gebruiken zoals elke andere Java Enum:

EnumSingleton enumSingleton1 = EnumSingleton.INSTANCE.getInstance (); System.out.println (enumSingleton1.getInfo ()); // Initiële enum info EnumSingleton enumSingleton2 = EnumSingleton.INSTANCE.getInstance (); enumSingleton2.setInfo ("Nieuwe enum info"); System.out.println (enumSingleton1.getInfo ()); // Nieuwe enum info System.out.println (enumSingleton2.getInfo ()); // Nieuwe enum-info

5. Veelvoorkomende valkuilen

Singleton is een bedrieglijk eenvoudig ontwerppatroon en er zijn enkele veelvoorkomende fouten die een programmeur zou kunnen maken bij het maken van een singleton.

We onderscheiden twee soorten problemen met eenlingen:

  • existentieel (hebben we een singleton nodig?)
  • implementatief (implementeren we het op de juiste manier?)

5.1. Existentiële problemen

Conceptueel is een singleton een soort globale variabele. Over het algemeen weten we dat globale variabelen moeten worden vermeden - vooral als hun toestanden veranderlijk zijn.

We zeggen niet dat we nooit singletons mogen gebruiken. We zeggen echter dat er wellicht efficiëntere manieren zijn om onze code te ordenen.

Als de implementatie van een methode afhangt van een singleton-object, waarom zou je het dan niet als parameter doorgeven? In dit geval laten we expliciet zien waar de methode van afhangt. Als gevolg hiervan kunnen we deze afhankelijkheden gemakkelijk bespotten (indien nodig) bij het uitvoeren van testen.

Singletons worden bijvoorbeeld vaak gebruikt om de configuratiegegevens van de applicatie te omvatten (d.w.z. verbinding met de repository). Als ze als globale objecten worden gebruikt, wordt het moeilijk om de configuratie voor de testomgeving te kiezen.

Daarom raakt de productiedatabase tijdens het uitvoeren van de tests verwend met de testgegevens, wat nauwelijks acceptabel is.

Als we een singleton nodig hebben, zouden we de mogelijkheid kunnen overwegen om de instantiatie ervan te delegeren aan een andere klasse - een soort fabriek - die ervoor zou moeten zorgen dat er slechts één exemplaar van de singleton in het spel is.

5.2. Implementatieproblemen

Hoewel de eenlingen vrij eenvoudig lijken, kunnen hun implementaties aan verschillende problemen lijden. Alle resulteren in het feit dat we uiteindelijk meer dan één instantie van de klas kunnen hebben.

Synchronisatie

De implementatie met een privé-constructor die we hierboven hebben gepresenteerd, is niet thread-safe: het werkt goed in een omgeving met één thread, maar in een multi-threaded omgeving moeten we de synchronisatietechniek gebruiken om de atomiciteit van de bewerking te garanderen:

openbare gesynchroniseerde statische ClassSingleton getInstance () {if (INSTANCE == null) {INSTANCE = nieuwe ClassSingleton (); } keer INSTANCE terug; }

Let op het sleutelwoord gesynchroniseerd in de methode-declaratie. De hoofdtekst van de methode heeft verschillende bewerkingen (vergelijking, instantiëring en terugkeer).

Als er geen synchronisatie is, is er een mogelijkheid dat twee threads hun uitvoeringen zodanig door elkaar laten lopen dat de uitdrukking INSTANCE == null evalueert naar waar voor beide threads en, als resultaat, twee gevallen van ClassSingleton word gemaakt.

Synchronisatie kan de prestatie aanzienlijk beïnvloeden. Als deze code vaak wordt aangeroepen, moeten we deze versnellen met behulp van verschillende technieken, zoals luie initialisatie of dubbelgecontroleerde vergrendeling (houd er rekening mee dat dit mogelijk niet werkt zoals verwacht vanwege compileroptimalisaties). We kunnen meer details zien in onze tutorial "Double-Checked Locking with Singleton".

Meerdere instanties

Er zijn verschillende andere problemen met de singletons die verband houden met JVM zelf, waardoor we kunnen eindigen met meerdere exemplaren van een singleton. Deze problemen zijn vrij subtiel en we zullen voor elk ervan een korte beschrijving geven:

  1. Een singleton wordt verondersteld uniek te zijn per JVM. Dit kan een probleem zijn voor gedistribueerde systemen of systemen waarvan de interne onderdelen zijn gebaseerd op gedistribueerde technologieën.
  2. Elke klassenlader kan zijn versie van de singleton laden.
  3. Een singleton kan als vuilnis worden opgehaald als niemand er een verwijzing naar heeft. Dit probleem leidt niet tot de aanwezigheid van meerdere singleton-exemplaren tegelijk, maar wanneer het opnieuw wordt gemaakt, kan het exemplaar verschillen van de vorige versie.

6. Conclusie

In deze korte tutorial hebben we ons gericht op het implementeren van het Singleton-patroon met alleen kern-Java, en hoe je ervoor kunt zorgen dat het consistent is en hoe je deze implementaties kunt gebruiken.

De volledige implementatie van deze voorbeelden is te vinden op GitHub.


$config[zx-auto] not found$config[zx-overlay] not found