Dvostruko provjereno zaključavanje Singletonom

1. Uvod

U ovom uputstvu razgovarat ćemo o dvostruko provjerenom uzorku dizajna zaključavanja. Ovaj obrazac smanjuje broj stjecanja brava jednostavnim provjerom stanja zaključavanja unaprijed. Kao rezultat toga, obično dolazi do poboljšanja performansi.

Pogledajmo dublje kako to funkcionira.

2. Provedba

Za početak, razmotrimo jednostavan singleton s drakonskom sinkronizacijom:

javna klasa DraconianSingleton {privatna statička instanca DraconianSingleton; javni statički sinkronizirani DraconianSingleton getInstance () {if (instance == null) {instance = new DraconianSingleton (); } povratna instanca; } // privatni konstruktor i druge metode ...}

Unatoč tome što je ova klasa zaštićena od niti, možemo primijetiti da postoji očit nedostatak u izvedbi: svaki put kad želimo dobiti instancu našeg singletona, moramo steći potencijalno nepotrebnu bravu.

Da biste to popravili, umjesto toga mogli bismo započeti s provjerom trebamo li uopće stvarati objekt i samo bismo u tom slučaju stekli bravu.

Idući dalje, želimo ponovo izvršiti istu provjeru čim uđemo u sinkronizirani blok, kako bi operacija ostala atomska:

javna klasa DclSingleton {privatna statička nestabilna instanca DclSingleton; javni statički DclSingleton getInstance () {if (instance == null) {sinkronizirano (DclSingleton .class) {if (instance == null) {instance = new DclSingleton (); }}} return instanca; } // privatni konstruktor i druge metode ...}

Jedna stvar koju treba imati na umu kod ovog uzorka je ta polje treba biti hlapljiv kako bi se spriječile neprikladne predmemorije Zapravo, Java model memorije omogućuje objavljivanje djelomično inicijaliziranih objekata, što bi zauzvrat moglo dovesti do suptilnih bugova.

3. Alternative

Iako dvostruko provjereno zaključavanje potencijalno može ubrzati stvari, ima barem dva problema:

  • budući da zahtijeva hlapljiv ključna riječ radi ispravno, nije kompatibilna s Javom 1.4 i starijim verzijama
  • prilično je opširan i otežava čitanje koda

Iz tih razloga, pogledajmo neke druge mogućnosti bez tih nedostataka. Sve sljedeće metode delegiraju zadatak sinkronizacije na JVM.

3.1. Rana inicijalizacija

Najjednostavniji način postizanja sigurnosti niti je ugrađivanje stvaranja objekta ili upotreba ekvivalentnog statičkog bloka. Ovo iskorištava činjenicu da se statička polja i blokovi inicijaliziraju jedno za drugim (Java Specifikacija jezika 12.4.2):

javna klasa EarlyInitSingleton {privatni statički konačni EarlyInitSingleton INSTANCE = novi EarlyInitSingleton (); javna statička EarlyInitSingleton getInstance () {return INSTANCE; } // privatni konstruktor i druge metode ...}

3.2. Inicijalizacija na zahtjev

Uz to, budući da iz reference Java Specifikacije jezika u prethodnom odlomku znamo da se inicijalizacija klase događa prvi put kada koristimo jednu od njezinih metoda ili polja, možemo koristiti ugniježđenu statičku klasu za provedbu lijene inicijalizacije:

javna klasa InitOnDemandSingleton {privatna statička klasa InstanceHolder {privatna statička konačna InitOnDemandSingleton INSTANCE = novi InitOnDemandSingleton (); } javni statični InitOnDemandSingleton getInstance () {return InstanceHolder.INSTANCE; } // privatni konstruktor i druge metode ...}

U ovom slučaju, InstanceHolder klasa će dodijeliti polje prvi put kad mu pristupimo pozivanjem getInstance.

3.3. Enum Singleton

Posljednje rješenje dolazi iz Učinkovita Java knjiga (Stavka 3) Joshua Blocka i koristi an nabrajanje umjesto a razred. U vrijeme pisanja ovog teksta, ovo se smatra najsažetijim i najsigurnijim načinom pisanja jednog slova:

javni popis EnumSingleton {INSTANCE; // ostale metode ...}

4. Zaključak

Da rezimiramo, ovaj je brzi članak prošao kroz dvostruko provjereni obrazac zaključavanja, njegova ograničenja i neke alternative.

U praksi prekomjerna opširnost i nedostatak povratne kompatibilnosti čine ovaj obrazac sklonim pogreškama i stoga bismo ga trebali izbjegavati. Umjesto toga, trebali bismo koristiti alternativu koja JVM-u omogućuje sinkronizaciju.

Kao i uvijek, kod svih primjera dostupan je na GitHubu.