Vad är en singleton i Java?
Singleton är ett av de enklaste designmönstren på klassnivå. Ibland säger folk "den här klassen är singleton", vilket betyder att klassen implementerar singeltondesignmönstret. Ibland är det nödvändigt att skriva en klass där vi begränsar instansieringen till ett enda objekt. Till exempel en klass som ansvarar för att logga eller ansluta till en databas. Singleton-designmönstret beskriver hur vi kan uppnå detta. En singleton är ett designmönster som gör två saker:-
Det garanterar att det bara någonsin kommer att finnas en instans av klassen.
-
Det ger en enda punkt för global åtkomst till den instansen.
-
En privat konstruktör. Detta begränsar möjligheten att skapa objekt av klassen utanför själva klassen.
-
En offentlig statisk metod som returnerar klassens instans. Denna metod kallas getInstance . Detta är punkten för global åtkomst till klassinstansen.
Genomförandealternativ
Singleton-designmönstret appliceras på olika sätt. Varje alternativ är bra och dåligt på sitt sätt. Som alltid finns det inget perfekt alternativ här, men vi bör sträva efter ett. Låt oss först och främst bestämma vad som är bra och dåligt, och vilka mått som påverkar hur vi bedömer de olika implementeringarna av designmönstret. Låt oss börja med det goda. Här är faktorer som gör en implementering mer saftig och tilltalande:-
Lat initialisering: instansen skapas inte förrän den behövs.
-
Enkel och transparent kod: detta mått är naturligtvis subjektivt, men det är viktigt.
-
Trådsäkerhet: korrekt funktion i en flertrådig miljö.
-
Hög prestanda i en flertrådig miljö: liten eller ingen trådblockering vid delning av en resurs.
-
Ingen lat initiering: när klassen laddas när applikationen startar, oavsett om den behövs eller inte (paradoxalt nog, i IT-världen är det bättre att vara lat)
-
Komplex och svårläst kod. Detta mått är också subjektivt. Om dina ögon börjar blöda, antar vi att implementeringen inte är den bästa.
-
Brist på trådsäkerhet. Med andra ord "trådfara". Felaktig funktion i en flertrådig miljö.
-
Dålig prestanda i en miljö med flera trådar: trådar blockerar varandra hela tiden eller ofta när de delar en resurs.
Koda
Nu är vi redo att överväga olika implementeringsalternativ och ange för- och nackdelar:Enkel
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
Den enklaste implementeringen. Fördelar:
-
Enkel och transparent kod
-
Trådsäkerhet
-
Hög prestanda i en flertrådig miljö
- Ingen lat initiering.
Lat initiering
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Fördelar:
-
Lat initiering.
-
Inte trådsäker
Synkroniserad åtkomst
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Fördelar:
-
Lat initiering.
-
Trådsäkerhet
-
Dålig multitrådsprestanda
Dubbelkollad låsning
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;
}
}
Fördelar:
-
Lat initiering.
-
Trådsäkerhet
-
Hög prestanda i en flertrådig miljö
-
Stöds inte i tidigare versioner av Java under 1.5 (användningen av flyktiga nyckelord är fast sedan version 1.5)
Klasshållare
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;
}
}
Fördelar:
-
Lat initiering.
-
Trådsäkerhet.
-
Hög prestanda i en flertrådig miljö.
-
Korrekt drift kräver en garanti för att singleton -objektet initieras utan fel. Annars kommer det första anropet till getInstance -metoden att resultera i en ExceptionInInitializerError , och alla efterföljande anrop kommer att producera en NoClassDefFoundError .
Genomförande | Lat initiering | Trådsäkerhet | Multitrådad prestanda | När ska man använda? |
---|---|---|---|---|
Enkel | - | + | Snabb | Aldrig. Eller möjligen när lat initiering inte är viktigt. Men det skulle aldrig bli bättre. |
Lat initiering | + | - | Inte tillämpbar | Alltid när flertrådning inte behövs |
Synkroniserad åtkomst | + | + | Långsam | Aldrig. Eller möjligen när multithreaded prestanda inte spelar någon roll. Men det skulle aldrig bli bättre. |
Dubbelkollad låsning | + | + | Snabb | I sällsynta fall när du behöver hantera undantag när du skapar singleton (när klassinnehavaren singleton inte är tillämplig) |
Klasshållare | + | + | Snabb | Närhelst multithreading behövs och det finns en garanti för att singleton-objektet kommer att skapas utan problem. |
För- och nackdelar med singelmönstret
I allmänhet gör en singel exakt vad som förväntas av den:-
Det garanterar att det bara någonsin kommer att finnas en instans av klassen.
-
Det ger en enda punkt för global åtkomst till den instansen.
-
En singleton bryter mot principen om ett enda ansvar: förutom sina direkta uppgifter kontrollerar singelklassen också antalet instanser.
-
En vanlig klasss beroende av en singel syns inte i klassens offentliga kontrakt.
-
Globala variabler är dåliga. I slutändan förvandlas en singel till en rejäl global variabel.
-
Närvaron av en singel minskar testbarheten för applikationen som helhet och klasserna som använder singeln i synnerhet.
GO TO FULL VERSION