Zamknutie s jednou kontrolou so Singletonom

1. Úvod

V tomto tutoriáli si povieme o dvakrát skontrolovanom dizajnovom vzore zamykania. Tento vzor znižuje počet získaní zámku jednoduchou kontrolou stavu blokovania vopred. Výsledkom je zvyčajne zvýšenie výkonu.

Pozrime sa podrobnejšie na to, ako to funguje.

2. Implementácia

Na začiatok zvážime jednoduchý singleton s drakonickou synchronizáciou:

verejná trieda DraconianSingleton {súkromná statická inštancia DraconianSingleton; public static synchronized DraconianSingleton getInstance () {if (instance == null) {instance = new DraconianSingleton (); } návratová inštancia; } // súkromný konštruktor a ďalšie metódy ...}

Napriek tomu, že je táto trieda bezpečná pre vlákna, vidíme, že existuje jasná nevýhoda výkonu: zakaždým, keď chceme získať inštanciu nášho singletonu, musíme získať potenciálne nepotrebný zámok.

Ak to chcete napraviť, mohli by sme namiesto toho začať overením, či je potrebné vytvoriť objekt na prvom mieste, a až v takom prípade by sme získali zámok.

Keď ideme ďalej, chceme vykonať rovnakú kontrolu znova, akonáhle vstúpime do synchronizovaného bloku, aby operácia zostala atómová:

verejná trieda DclSingleton {súkromná statická volatilná inštancia DclSingleton; public static DclSingleton getInstance () {if (instance == null) {synchronizované (DclSingleton .class) {if (instance == null) {instance = new DclSingleton (); }}} návratová inštancia; } // súkromný konštruktor a ďalšie metódy ...}

Pri tomto vzore treba mať na pamäti jednu vec pole musí byť prchavý aby sa zabránilo problémom s nesúladom pamäte cache. Pamäťový model Java v skutočnosti umožňuje publikovanie čiastočne inicializovaných objektov, čo môže viesť k jemným chybám.

3. Alternatívy

Aj keď dvojito skontrolované zamykanie môže veci potenciálne urýchliť, má minimálne dva problémy:

  • pretože to vyžaduje prchavý aby kľúčové slovo fungovalo správne, nie je kompatibilné s Java 1.4 a nižšími verziami
  • je dosť podrobný a sťažuje čítanie kódu

Z týchto dôvodov sa pozrime na niektoré ďalšie možnosti bez týchto nedostatkov. Všetky nasledujúce metódy delegujú úlohu synchronizácie na JVM.

3.1. Včasná inicializácia

Najjednoduchší spôsob, ako dosiahnuť bezpečnosť vlákna, je vložiť vytvorenie objektu alebo použiť ekvivalentný statický blok. Toto využíva skutočnosť, že statické polia a bloky sa inicializujú jeden po druhom (Java Language Specification 12.4.2):

verejná trieda EarlyInitSingleton {súkromné ​​statické finále EarlyInitSingleton INSTANCE = nový EarlyInitSingleton (); public static EarlyInitSingleton getInstance () {návrat INSTANCE; } // súkromný konštruktor a ďalšie metódy ...}

3.2. Inicializácia na požiadanie

Ďalej, keďže z odkazu na špecifikáciu jazyka Java v predchádzajúcom odseku vieme, že k inicializácii triedy dôjde pri prvom použití jednej z jej metód alebo polí, môžeme na implementáciu lenivej inicializácie použiť vnorenú statickú triedu:

public class InitOnDemandSingleton {private static class InstanceHolder {private static final InitOnDemandSingleton INSTANCE = new InitOnDemandSingleton (); } public static InitOnDemandSingleton getInstance () {return InstanceHolder.INSTANCE; } // súkromný konštruktor a ďalšie metódy ...}

V takom prípade InstanceHolder trieda priradí pole k prvému vyvolaniu vyvolaním getInstance.

3.3. Enum Singleton

Posledné riešenie pochádza z Efektívna Java kniha (položka 3) od Joshua Bloka a používa knihu enum namiesto a trieda. V čase písania tohto článku sa to považuje za najvýstižnejší a najbezpečnejší spôsob písania singletonu:

public enum EnumSingleton {INSTANCE; // ďalšie metódy ...}

4. Záver

Ak to zhrnieme, tento rýchly článok prešiel dvojito skontrolovaným blokovacím vzorom, jeho obmedzeniami a niektorými alternatívami.

V praxi je kvôli nadmernej výrečnosti a nedostatočnej spätnej kompatibilite tento vzor náchylný na chyby, a preto by sme sa mu mali vyhnúť. Namiesto toho by sme mali použiť alternatívu, ktorá umožňuje JVM synchronizáciu.

Kód všetkých príkladov je ako vždy k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found