CodeGym /Java Blog /Willekeurig /Java Singleton-klasse
John Squirrels
Niveau 41
San Francisco

Java Singleton-klasse

Gepubliceerd in de groep Willekeurig
Hoi! Vandaag duiken we in de details van verschillende ontwerppatronen, te beginnen met het Java Singleton-patroon. Laten we eens kijken: wat weten we over ontwerppatronen in het algemeen? Design patterns zijn best practices die we kunnen toepassen om een ​​aantal bekende problemen op te lossen. Ontwerppatronen zijn over het algemeen niet gebonden aan een programmeertaal. Zie ze als een reeks aanbevelingen om u te helpen fouten te voorkomen en te voorkomen dat u het wiel opnieuw uitvindt.Ontwerppatronen: Singleton - 1

Wat is een singleton in Java?

Singleton is een van de eenvoudigste ontwerppatronen op klasseniveau. Soms zeggen mensen "deze klasse is singleton", wat betekent dat de klasse het singleton-ontwerppatroon implementeert. Soms is het nodig om een ​​klasse te schrijven waarbij we de instantiëring beperken tot een enkel object. Bijvoorbeeld een klasse die verantwoordelijk is voor het loggen of verbinden met een database. Het singleton-ontwerppatroon beschrijft hoe we dit kunnen bereiken. Een singleton is een ontwerppatroon dat twee dingen doet:
  1. Het garandeert dat er maar één instantie van de klasse zal zijn.

  2. Het biedt een enkel punt voor wereldwijde toegang tot die instantie.

Daarom zijn er twee kenmerken die kenmerkend zijn voor bijna elke implementatie van het singleton-patroon:
  1. Een particuliere constructeur. Dit beperkt de mogelijkheid om objecten van de klasse buiten de klasse zelf te maken.

  2. Een openbare statische methode die de instantie van de klasse retourneert. Deze methode wordt getInstance genoemd . Dit is het punt van globale toegang tot de klasse-instantie.

Implementatie opties

Het singleton-ontwerppatroon wordt op verschillende manieren toegepast. Elke optie is op zijn eigen manier goed en slecht. Zoals altijd is er hier geen perfecte optie, maar we moeten ernaar streven. Laten we eerst eens kijken wat goed en slecht is, en welke maatstaven van invloed zijn op hoe we de verschillende implementaties van het ontwerppatroon beoordelen. Laten we beginnen met het goede. Hier zijn factoren die een implementatie sappiger en aantrekkelijker maken:
  • Luie initialisatie: de instantie wordt pas gemaakt als deze nodig is.

  • Eenvoudige en transparante code: deze statistiek is natuurlijk subjectief, maar wel belangrijk.

  • Draadveiligheid: juiste werking in een omgeving met meerdere threads.

  • Hoge prestaties in een omgeving met meerdere threads: weinig of geen threadblokkering bij het delen van een bron.

Nu de nadelen. We sommen factoren op die een implementatie in een slecht daglicht stellen:
  • Geen luie initialisatie: wanneer de klasse wordt geladen wanneer de applicatie start, ongeacht of het nodig is of niet (paradoxaal genoeg is het in de IT-wereld beter om lui te zijn)

  • Complexe en moeilijk leesbare code. Deze statistiek is ook subjectief. Als je ogen beginnen te bloeden, gaan we ervan uit dat de implementatie niet de beste is.

  • Gebrek aan draadveiligheid. Met andere woorden, "draadgevaar". Onjuiste werking in een omgeving met meerdere threads.

  • Slechte prestaties in een omgeving met meerdere threads: threads blokkeren elkaar de hele tijd of vaak bij het delen van een bron.

Code

Nu zijn we klaar om verschillende implementatieopties te overwegen en de voor- en nadelen aan te geven:

Eenvoudig


public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {
    }
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}
De eenvoudigste uitvoering. Voordelen:
  • Eenvoudige en transparante code

  • Draad veiligheid

  • Hoge prestaties in een omgeving met meerdere threads

Nadelen:
  • Geen luie initialisatie.
In een poging om de vorige tekortkoming te verhelpen, krijgen we implementatie nummer twee:

Luie initialisatie


public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Voordelen:
  • Luie initialisatie.

Nadelen:
  • Niet draadveilig

Deze uitvoering is interessant. We kunnen lui initialiseren, maar we hebben de draadbeveiliging verloren. Geen zorgen - we synchroniseren alles in implementatie nummer drie.

Gesynchroniseerde toegang


public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Voordelen:
  • Luie initialisatie.

  • Draad veiligheid

Nadelen:
  • Slechte multithreaded prestaties

Uitstekend! In implementatie nummer drie herstellen we de draadveiligheid! Het is natuurlijk langzaam... Nu is de methode getInstance gesynchroniseerd, zodat deze door slechts één thread tegelijk kan worden uitgevoerd. In plaats van de hele methode te synchroniseren, hoeven we eigenlijk alleen het deel ervan te synchroniseren dat de nieuwe instantie initialiseert. Maar we kunnen niet zomaar een gesynchroniseerd blok gebruiken om het deel dat verantwoordelijk is voor het maken van de nieuwe instantie in te pakken. Als u dat zou doen, zou de draadveiligheid niet worden gegarandeerd. Het is allemaal wat ingewikkelder. De juiste synchronisatie is hieronder te zien:

Dubbel gecontroleerde vergrendeling


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;
    }
}
Voordelen:
  • Luie initialisatie.

  • Draad veiligheid

  • Hoge prestaties in een omgeving met meerdere threads

Nadelen:
  • Niet ondersteund in eerdere versies van Java onder 1.5 (het gebruik van vluchtig trefwoord is opgelost sinds versie 1.5)

Houd er rekening mee dat om deze implementatieoptie correct te laten werken, aan een van de twee voorwaarden moet worden voldaan. De variabele INSTANCE moet definitief of vluchtig zijn . De laatste implementatie die we vandaag bespreken, is de klassehouder singleton .

Klasse houder


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;
   }
}
Voordelen:
  • Luie initialisatie.

  • Draad veiligheid.

  • Hoge prestaties in een omgeving met meerdere threads.

Nadelen:
  • Correcte werking vereist een garantie dat het singleton- object foutloos wordt geïnitialiseerd. Anders resulteert de eerste aanroep van de methode getInstance in een ExceptionInInitializerError en alle volgende aanroepen produceren een NoClassDefFoundError .

Deze uitvoering is bijna perfect. Het is lui, draadveilig en snel. Maar het heeft een nuance, zoals uitgelegd in de lijst met nadelen. Vergelijking van verschillende implementaties van het singleton-patroon:
Implementatie Luie initialisatie Draad veiligheid Multithreaded-prestaties Wanneer te gebruiken?
Eenvoudig - + Snel Nooit. Of mogelijk wanneer luie initialisatie niet belangrijk is. Maar nooit zou beter zijn.
Luie initialisatie + - Niet toepasbaar Altijd als multithreading niet nodig is
Gesynchroniseerde toegang + + Langzaam Nooit. Of mogelijk wanneer multithreaded prestaties er niet toe doen. Maar nooit zou beter zijn.
Dubbel gecontroleerde vergrendeling + + Snel In zeldzame gevallen wanneer u uitzonderingen moet afhandelen bij het maken van de singleton (wanneer de klassehouder singleton niet van toepassing is)
Klasse houder + + Snel Telkens wanneer multithreading nodig is en er een garantie is dat het singleton-object zonder problemen wordt gemaakt.

Voors en tegens van het singleton-patroon

Over het algemeen doet een singleton precies wat er van wordt verwacht:
  1. Het garandeert dat er maar één instantie van de klasse zal zijn.

  2. Het biedt een enkel punt voor wereldwijde toegang tot die instantie.

Dit patroon heeft echter tekortkomingen:
  1. Een singleton schendt het single responsibility-principe: naast zijn directe taken controleert de singleton-klasse ook het aantal instanties.

  2. De afhankelijkheid van een gewone klas van een singleton is niet zichtbaar in het openbare contract van de klas.

  3. Globale variabelen zijn slecht. Uiteindelijk verandert een singleton in een forse globale variabele.

  4. De aanwezigheid van een singleton vermindert de testbaarheid van de applicatie als geheel en de klassen die de singleton in het bijzonder gebruiken.

En dat is het! :) We hebben de Java Singleton Class met je verkend. Nu, voor de rest van je leven, als je met je programmeursvrienden praat, kun je niet alleen vermelden hoe goed het patroon is, maar ook een paar woorden over wat het slecht maakt. Veel succes met het beheersen van deze nieuwe kennis.

Extra lectuur:

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