CodeGym /Java blogg /Slumpmässig /Java Singleton klass
John Squirrels
Nivå
San Francisco

Java Singleton klass

Publicerad i gruppen
Hej! Idag ska vi dyka in i detaljerna i olika designmönster, med början med Java Singleton-mönstret. Låt oss granska: vad vet vi om designmönster i allmänhet? Designmönster är bästa praxis som vi kan tillämpa för att lösa ett antal kända problem. Designmönster är i allmänhet inte bundna till något programmeringsspråk. Se dem som en uppsättning rekommendationer som hjälper dig att undvika misstag och undvika att uppfinna hjulet på nytt.Designmönster: Singleton - 1

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:
  1. Det garanterar att det bara någonsin kommer att finnas en instans av klassen.

  2. Det ger en enda punkt för global åtkomst till den instansen.

Därför finns det två egenskaper som är karakteristiska för nästan varje implementering av singelmönstret:
  1. En privat konstruktör. Detta begränsar möjligheten att skapa objekt av klassen utanför själva klassen.

  2. 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.

Nu nackdelarna. Vi listar faktorer som ställer en implementering i dåligt ljus:
  • 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ö

Nackdelar:
  • Ingen lat initiering.
I ett försök att åtgärda den tidigare bristen får vi implementering nummer två:

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.

Nackdelar:
  • Inte trådsäker

Denna implementering är intressant. Vi kan initiera lat, men vi har tappat trådsäkerheten. Inga bekymmer — vi synkroniserar allt i implementering nummer tre.

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

Nackdelar:
  • Dålig multitrådsprestanda

Excellent! I implementering nummer tre återställer vi trådsäkerheten! Naturligtvis är det långsamt... Nu är getInstance- metoden synkroniserad, så den kan köras av endast en tråd åt gången. Istället för att synkronisera hela metoden behöver vi faktiskt bara synkronisera den del av den som initierar den nya instansen. Men vi kan inte bara använda ett synkroniserat block för att linda den del som är ansvarig för att skapa den nya instansen. Att göra det skulle inte garantera trådsäkerheten. Det hela är lite mer komplicerat. Korrekt synkronisering kan ses nedan:

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ö

Nackdelar:
  • Stöds inte i tidigare versioner av Java under 1.5 (användningen av flyktiga nyckelord är fast sedan version 1.5)

Observera att för att detta implementeringsalternativ ska fungera korrekt måste ett av två villkor vara uppfyllt. Variabeln INSTANCE måste vara antingen final eller volatil . Den sista implementeringen som vi kommer att diskutera idag är klasshållaren singleton .

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ö.

Nackdelar:
  • 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 .

Denna implementering är nästan perfekt. Det är lat, och trådsäkert och snabbt. Men det har en nyans, som förklaras i listan över nackdelar. Jämförelse av olika implementeringar av singelmönstret:
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:
  1. Det garanterar att det bara någonsin kommer att finnas en instans av klassen.

  2. Det ger en enda punkt för global åtkomst till den instansen.

Detta mönster har dock brister:
  1. En singleton bryter mot principen om ett enda ansvar: förutom sina direkta uppgifter kontrollerar singelklassen också antalet instanser.

  2. En vanlig klasss beroende av en singel syns inte i klassens offentliga kontrakt.

  3. Globala variabler är dåliga. I slutändan förvandlas en singel till en rejäl global variabel.

  4. Närvaron av en singel minskar testbarheten för applikationen som helhet och klasserna som använder singeln i synnerhet.

Och det är allt! :) Vi har utforskat Java Singleton Class med dig. Nu, för resten av ditt liv, när du samtalar med dina programmerarvänner, kan du inte bara nämna hur bra mönstret är, utan också några ord om vad som gör det dåligt. Lycka till med att bemästra denna nya kunskap.

Ytterligare läsning:

Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION