Programovanie

Vytvorte si svoj vlastný ObjectPool v Jave, 1. časť

Myšlienka združovania objektov je podobná ako fungovanie vašej miestnej knižnice: Keď si chcete prečítať knihu, viete, že je lacnejšie požičať si kópiu z knižnice ako kúpiť vlastnú kópiu. Rovnako je to pre proces lacnejšie (vo vzťahu k pamäti a rýchlosti) požičať objekt namiesto vytvárania vlastnej kópie. Inými slovami, knihy v knižnici predstavujú objekty a patróni knižnice procesy. Ak proces potrebuje objekt, namiesto vytvorenia inštancie novej namiesto nej rezervuje kópiu z fondu objektov. Proces potom vráti objekt do oblasti, keď už nie je potrebný.

Existuje však niekoľko menších rozdielov medzi združovaním objektov a analógiou knižnice, ktorým by sa malo rozumieť. Ak si čitateľ knižnice želá konkrétnu knihu, ale všetky kópie tejto knihy sú rezervované, musí si počkať, kým sa kópia nevráti. Nikdy nechceme, aby proces musel čakať na objekt, takže fond objektov podľa potreby vytvorí nové kópie. To by mohlo viesť k nadmernému množstvu predmetov ležiacich v bazéne, takže tiež bude udržiavať záznam o nepoužívaných predmetoch a pravidelne ich čistiť.

Môj návrh fondu objektov je dostatočne všeobecný na to, aby zvládol časy ukladania, sledovania a vypršania platnosti, ale s inštanciami, overovaním a zničením konkrétnych typov objektov sa musí zaobchádzať podtriedou.

Teraz, keď máte základy z cesty, poďme skočiť do kódu. Toto je kostrový objekt:

 verejná abstraktná trieda ObjectPool {private long expirationTime; súkromná tabuľka hash uzamknutá, odomknutá; abstraktný objekt create (); abstraktné logické overenie (Objekt o); abstraktná neplatnosť vypršala (Objekt o); synchronized Object checkOut () {...} synchronizovaný neplatný checkIn (objekt o) {...}} 

Interné úložisko združených objektov bude riešené dvoma Hashtable objekty, jeden pre uzamknuté objekty a druhý pre odomknuté. Samotné objekty budú kľúčmi hashtable a ich posledným časom použitia (v epochových milisekundách) bude hodnota. Uložením posledného použitia objektu ho môže fond expirovať a uvoľniť pamäť po stanovenej dobe nečinnosti.

Fond objektov by v konečnom dôsledku umožnil podtriede určiť počiatočnú veľkosť hashtable spolu s ich rýchlosťou rastu a časom expirácie, ale snažím sa to na účely tohto článku zjednodušiť tak, že tieto hodnoty napevno napíšem konštruktér.

 ObjectPool () {expirationTime = 30000; // 30 sekúnd zamknuté = nový Hashtable (); unlocked = new Hashtable (); } 

The odhlásiť sa() metóda najskôr skontroluje, či sa v odomknutej hashtable nachádzajú nejaké objekty. Ak je to tak, cykluje ich a hľadá platnú. Validácia závisí od dvoch vecí. Po prvé, fond objektov skontroluje, či čas posledného použitia objektu nepresahuje čas vypršania špecifikovaný podtriedou. Po druhé, fond objektov nazýva abstrakt potvrdiť() metóda, ktorá vykonáva akúkoľvek kontrolu alebo reinicializáciu špecifickú pre danú triedu, ktorá je potrebná na opätovné použitie objektu. Ak validácia objektu zlyhá, uvoľní sa a slučka pokračuje k ďalšiemu objektu v hashtable. Keď sa nájde objekt, ktorý prejde validáciou, presunie sa do uzamknutej hashtable a vráti sa do procesu, ktorý o to požiadal. Ak je odomknutý hashtable prázdny alebo žiadny z jeho objektov neprejde validáciou, vytvorí sa nový objekt a vráti sa späť.

 synchronized Object checkOut () {dlho teraz = System.currentTimeMillis (); Objekt o; if (unlocked.size ()> 0) {Enumeration e = unlocked.keys (); while (e.hasMoreElements ()) {o = e.nextElement (); if ((now - ((Long) unlocked.get (o)) .longValue ())> expirationTime) {// objektu vypršala doba platnosti unlocked.remove (o); expirovať (o); o = null; } else {if (validate (o)) {unlocked.remove (o); uzamknutý.výstup (o, nový Long (teraz)); návrat (o); } else {// zlyhalo overenie objektu unlocked.remove (o); expirovať (o); o = null; }}}} // nie sú k dispozícii žiadne objekty, vytvoriť nový o = create (); uzamknutý.výstup (o, nový Long (teraz)); návrat (o); } 

Toto je najkomplexnejšia metóda v ObjectPool triedy, odtiaľto je to všetko z kopca. The checkIn () metóda jednoducho presunie vložený objekt z uzamknutej hashtable do odomknutej hashtable.

synchronized void checkIn (Objekt o) {uzamknuté.odstrániť (o); unlocked.put (o, new Long (System.currentTimeMillis ())); } 

Tri zostávajúce metódy sú abstraktné, a preto ich musí podtrieda implementovať. V záujme tohto článku sa chystám vytvoriť fond databázových pripojení s názvom JDBCConnectionPool. Tu je kostra:

 public class JDBCConnectionPool extends ObjectPool {private String dsn, usr, pwd; public JDBCConnectionPool () {...} create () {...} validate () {...} expire () {...} public connection loanConnection () {...} public void returnConnection () {. ..}} 

The JDBCConnectionPool bude od aplikácie vyžadovať, aby po vytvorení inštancie zadala ovládač databázy, DSN, používateľské meno a heslo (prostredníctvom konštruktora). (Ak je to pre vás všetko grécke, nebojte sa, JDBC je iná téma. Len so mnou vydržte, kým sa nedostaneme späť k zhromaždeniu.)

 public JDBCConnectionPool (String driver, String dsn, String usr, String pwd) {try {Class.forName (driver) .newInstance (); } catch (Výnimka e) {e.printStackTrace (); } this.dsn = dsn; this.usr = usr; this.pwd = pwd; } 

Teraz sa môžeme ponoriť do implementácie abstraktných metód. Ako ste videli v odhlásiť sa() metóda, ObjectPool zavolá create () zo svojej podtriedy, keď potrebuje vytvoriť inštanciu nového objektu. Pre JDBCConnectionPool, všetko, čo musíme urobiť, je vytvoriť nový Pripojenie objekt a odovzdať ho späť. Opäť kvôli zjednodušeniu tohto článku vrhám opatrnosť do vetra a ignorujem všetky výnimky a podmienky nulového ukazovateľa.

 Object create () {try {return (DriverManager.getConnection (dsn, usr, pwd)); } catch (SQLException e) {e.printStackTrace (); návrat (null); }} 

Pred ObjectPool uvoľní expirovaný (alebo neplatný) objekt na zber odpadkov, odovzdá ho svojej podtriede vypršať() metóda nevyhnutného vyčistenia na poslednú chvíľu (veľmi podobná ako v dokončiť () metóda nazývaná smetiarom). V prípade JDBCConnectionPool, všetko, čo musíme urobiť, je uzavrieť spojenie.

void expire (Object o) {try {((Connection) o) .close (); } catch (SQLException e) {e.printStackTrace (); }} 

A nakoniec musíme implementovať metódu validate (), ktorá ObjectPool hovory, aby sa ubezpečil, že objekt je stále platný na použitie. Toto je tiež miesto, kde by malo dôjsť k akejkoľvek opätovnej inicializácii. Pre JDBCConnectionPool, len skontrolujeme, či je pripojenie stále otvorené.

 boolean validate (Object o) {try {return (! ((Connection) o) .isClosed ()); } catch (SQLException e) {e.printStackTrace (); návrat (nepravda); }} 

To je všetko pre internú funkčnosť. JDBCConnectionPool umožní aplikácii požičať si a vrátiť pripojenie k databáze pomocou týchto neuveriteľne jednoduchých a výstižne pomenovaných metód.

 verejné pripojenie loanConnection () {return ((Connection) super.checkOut ()); } public void returnConnection (pripojenie c) {super.checkIn (c); } 

Tento dizajn má niekoľko nedostatkov. Asi najväčšou je možnosť vytvorenia veľkého množstva objektov, ktoré sa nikdy neuvoľnia. Napríklad, ak veľa procesov požaduje objekt z fondu súčasne, fond vytvorí všetky potrebné inštancie. Potom, ak všetky procesy vrátia objekty späť do fondu, ale odhlásiť sa() už nikdy viac nezavoláme, žiadny z objektov nebude vyčistený. Pre aktívne aplikácie je to zriedkavý jav, ale niektoré scenáre, ktoré majú „nečinný“ čas, môžu vytvoriť tento scenár. Tento problém s dizajnom som vyriešil vláknom „clean up“, ale túto diskusiu si nechám pre druhú polovicu tohto článku. Ďalej sa budem venovať správnemu zaobchádzaniu s chybami a šíreniu výnimiek, aby bol fond robustnejší pre kritické aplikácie.

Thomas E. Davis je programátorom Java certifikovaným spoločnosťou Sun. Momentálne žije na slnečnej južnej Floride, ale trpí ako workoholik a väčšinu času trávi v interiéroch.

Tento príbeh „Vytvorte si svoj vlastný ObjectPool v Jave, časť 1“ pôvodne publikoval JavaWorld.

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