Singleton is een van de meest gebruikte creational design patronen om het object dat door applicaties wordt gemaakt, te beperken. Als je het gebruikt in een multi-threaded omgeving, is de thread-veiligheid van de singleton-klasse zeer belangrijk. In real-world applicaties zijn resources zoals Database-verbindingen of Enterprise Information Systems (EIS) beperkt en moeten ze verstandig worden gebruikt om resourceproblemen te voorkomen. Om dit te bereiken, kunnen we een Singleton design patroon implementeren. We kunnen een wrapperklasse voor de resource maken en het aantal objecten dat op runtime wordt gemaakt, beperken tot één.
Thread Safe Singleton in Java
Over het algemeen volgen we de onderstaande stappen om een singleton-klasse te maken:
- Maak de private constructor om nieuwe objectcreatie met de new-operator te voorkomen.
- Declareer een private static instantie van dezelfde klasse.
- public static method die de singleton class-instantievariabele retourneert. Als de variabele niet is geïnitialiseerd, initialiseer deze dan, anders retourneer eenvoudig de instantievariabele.
Met behulp van de bovenstaande stappen heb ik een singleton-klasse gemaakt die er als volgt uitziet. ASingleton.java
package com.journaldev.designpatterns;
public class ASingleton {
private static ASingleton instance = null;
private ASingleton() {
}
public static ASingleton getInstance() {
if (instance == null) {
instance = new ASingleton();
}
return instance;
}
}
In de bovenstaande code is de getInstance() methode niet thread-safe. Meerdere threads kunnen er tegelijkertijd toegang toe hebben. Voor de eerste paar threads, wanneer de instantievariabele niet is geïnitialiseerd, kunnen meerdere threads de if-lus betreden en meerdere instanties maken. Dit zal onze singleton-implementatie doorbreken.
Hoe thread-veiligheid bereiken in een Singleton-klasse?
Er zijn drie manieren om thread-veiligheid te bereiken.
- Maak de instantievariabele bij het laden van de klasse.
Voors:
- Thread-veiligheid zonder synchronisatie
- Gemakkelijk te implementeren
Nadelen:
- Vroege creatie van bronnen die mogelijk niet worden gebruikt in de applicatie.
- De clientapplicatie kan geen argument doorgeven, dus we kunnen het niet hergebruiken. Bijvoorbeeld, het hebben van een generieke singleton-klasse voor databaseverbinding waar de clientapplicatie database server eigenschappen levert.
- Synchroniseer de getInstance() methode.
Voors:
- Draadveiligheid is gegarandeerd.
- De clienttoepassing kan parameters doorgeven
- Lazy-initialisatie bereikt
Nadelen:
- Trage prestaties als gevolg van vergrendelings-overhead.
- Niet noodzakelijke synchronisatie die niet meer nodig is zodra de instantievariabele is geïnitialiseerd.
- Gebruik een gesynchroniseerd blok binnen de if-lus en een volatiel variabele
Voordelen:
- Draadveiligheid is gegarandeerd
- De clienttoepassing kan argumenten doorgeven
- Lazy-initialisatie bereikt
- Synchronisatie-overhead is minimaal en alleen van toepassing op de eerste paar threads wanneer de variabele nul is.
Nadelen:
- Extra if-voorwaarde
Als we kijken naar alle drie manieren om draadveiligheid te waarborgen, denk ik dat de derde optie de beste is. In dat geval ziet de aangepaste klasse er als volgt uit:
package com.journaldev.designpatterns;
public class ASingleton {
private static volatile ASingleton instance;
private static Object mutex = new Object();
private ASingleton() {
}
public static ASingleton getInstance() {
ASingleton result = instance;
if (result == null) {
synchronized (mutex) {
result = instance;
if (result == null)
instance = result = new ASingleton();
}
}
return result;
}
}
De lokale variabele result
lijkt overbodig. Maar het is er om de prestaties van onze code te verbeteren. In gevallen waar de instantie al is geïnitialiseerd (meestal), wordt het volatiel veld slechts één keer benaderd (vanwege “return result;” in plaats van “return instance;”). Dit kan de algehele prestatie van de methode verbeteren met wel 25 procent. Als je denkt dat er betere manieren zijn om dit te bereiken of als de draadveiligheid in de bovenstaande implementatie in gevaar is, geef dan commentaar en deel het met ons allemaal.
Bonus Tip
Tekenreeks is geen erg goede kandidaat om te worden gebruikt met het synchronisatiesleutelwoord. Dit komt omdat ze worden opgeslagen in een stringpool en we willen geen tekenreeks vergrendelen die mogelijk door een ander stuk code wordt gebruikt. Daarom gebruik ik een Object-variabele. Leer meer over synchronisatie en draadveiligheid in Java.
Je kunt meer Java-voorbeelden bekijken op onze GitHub-opslagplaats.
Source:
https://www.digitalocean.com/community/tutorials/thread-safety-in-java-singleton-classes