Programování

Vytvořte si svůj vlastní ObjectPool v Javě, část 1

Myšlenka sdružování objektů je podobná činnosti vaší místní knihovny: Když si chcete přečíst knihu, víte, že je levnější si půjčit kopii z knihovny, než si koupit vlastní kopii. Stejně tak je pro proces levnější (ve vztahu k paměti a rychlosti) půjčit si spíše než vytvořit vlastní kopii objektu. Jinými slovy, knihy v knihovně představují objekty a čtenáři knihovny představují procesy. Když proces potřebuje objekt, zkontroluje kopii z fondu objektů, místo aby vytvořil instanci nového. Proces poté vrátí objekt do fondu, když již není potřeba.

Existuje však několik drobných rozdílů mezi sdružováním objektů a analogií knihovny, kterým je třeba rozumět. Pokud čtenář knihovny chce konkrétní knihu, ale všechny kopie této knihy jsou rezervovány, musí čtenář počkat, dokud není kopie vrácena. Nikdy nechceme, aby proces musel čekat na objekt, takže fond objektů podle potřeby vytvoří instanci nových kopií. To by mohlo vést k exhoribantnímu množství předmětů, které by ležely v bazénu, takže také bude udržovat záznam o nepoužívaných předmětech a pravidelně je čistit.

Můj návrh fondu objektů je dostatečně obecný, aby zvládl doby skladování, sledování a vypršení platnosti, ale vytváření instancí, ověřování a ničení konkrétních typů objektů je třeba řešit podtřídou.

Nyní, když základy z cesty, pojďme skočit do kódu. Toto je kosterní objekt:

 veřejná abstraktní třída ObjectPool {private long expirationTime; soukromá Hashtable zamčená, odemčená; abstraktní objekt create (); abstraktní logická validace (Objekt o); abstraktní neplatnost vyprší (Objekt o); synchronized Object checkOut () {...} synchronized void checkIn (Object o) {...}} 

Interní úložiště sdružených objektů bude zpracováno dvěma Hashtable objekty, jeden pro uzamčené objekty a druhý pro odemčené. Samotné objekty budou klíče hashtable a jejich doba posledního použití (v epochových milisekundách) bude hodnota. Uložením posledního použití objektu jej fond může vypršet a uvolnit paměť po zadané době nečinnosti.

Nakonec by fond objektů umožnil podtřídě určit počáteční velikost hashtable spolu s jejich rychlostí růstu a dobou vypršení platnosti, ale snažím se to pro účely tohoto článku zjednodušit pevným kódováním těchto hodnot v konstruktor.

 ObjectPool () {expirationTime = 30000; // 30 sekund uzamčeno = nový Hashtable (); unlocked = new Hashtable (); } 

The Překontrolovat() metoda nejprve zkontroluje, zda jsou v odemčené hashtable nějaké objekty. Pokud ano, cykluje je a hledá platnou. Ověření závisí na dvou věcech. Nejprve zkontroluje fond objektů, aby zjistil, že doba posledního použití objektu nepřekročí dobu vypršení platnosti určenou podtřídou. Zadruhé, objektový fond volá abstrakt ověřit() metoda, která provádí jakoukoli třídu specifickou kontrolu nebo reinicializaci, která je nutná k opětovnému použití objektu. Pokud ověření objektu selže, uvolní se a smyčka pokračuje k dalšímu objektu v hashtable. Když je nalezen objekt, který projde ověřením, je přesunut do uzamčené hashtable a vrácen do procesu, který o něj požádal. Pokud je odemčený hashtable prázdný nebo žádný z jeho objektů neprojde validací, vytvoří se nový objekt a vrátí se.

 synchronized Object checkOut () {dlouho nyní = 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šela platnost unlocked.remove (o); expirovat (o); o = null; } else {if (validate (o)) {unlocked.remove (o); locked.put (o, nový Long (nyní)); návrat (o); } else {// selhalo ověření objektu unlocked.remove (o); expirovat (o); o = null; }}}} // nejsou k dispozici žádné objekty, vytvořit nový o = create (); locked.put (o, nový Long (nyní)); návrat (o); } 

To je nejsložitější metoda v ObjectPool třídy, odtud je to všechno z kopce. The checkIn () metoda jednoduše přesune vložený objekt z uzamčené hashtable do odemčené hashtable.

synchronized void checkIn (Object o) {locked.remove (o); unlocked.put (o, new Long (System.currentTimeMillis ())); } 

Tři zbývající metody jsou abstraktní, a proto musí být implementovány podtřídou. V zájmu tohoto článku vytvořím fond připojení k databázi s názvem JDBCConnectionPool. Tady 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 vyžadovat, aby aplikace po vytvoření instance (prostřednictvím konstruktoru) určila ovladač databáze, DSN, uživatelské jméno a heslo. (Pokud je to pro vás celé řecké, nebojte se, JDBC je další téma. Jen mějte se mnou, dokud se nedostaneme zpět ke sdružování.)

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

Nyní se můžeme ponořit do implementace abstraktních metod. Jak jste viděli v Překontrolovat() metoda, ObjectPool zavolá create () ze své podtřídy, když potřebuje vytvořit instanci nového objektu. Pro JDBCConnectionPoolvše, co musíme udělat, je vytvořit nový Spojení objekt a předejte jej zpět. Znovu, kvůli udržení tohoto článku jednoduchého, dávám opatrnost větru a ignoruji všechny výjimky a podmínky nulového ukazatele.

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

Před ObjectPool uvolní prošlý (nebo neplatný) objekt pro uvolňování paměti, předá jej do své podtřídy expirovat () metoda pro nezbytné vyčištění na poslední chvíli (velmi podobná metodě dokončit() metoda volaná garbage collectorem). V případě JDBCConnectionPool, vše, co musíme udělat, je uzavřít spojení.

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

A nakonec musíme implementovat metodu validate () ObjectPool volání, aby se ujistil, že objekt je stále platný pro použití. Toto je také místo, kde by měla proběhnout jakákoli nová inicializace. Pro JDBCConnectionPool, pouze zkontrolujeme, zda je připojení stále otevřené.

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

To je vše pro interní funkčnost. JDBCConnectionPool umožní aplikaci půjčit si a vrátit připojení k databázi pomocí těchto neuvěřitelně jednoduchých a výstižně pojmenovaných metod.

 public Connection loanConnection () {return ((Connection) super.checkOut ()); } public void returnConnection (připojení c) {super.checkIn (c); } 

Tento design má několik nedostatků. Snad největší je možnost vytvoření velké skupiny objektů, které se nikdy neuvolní. Například pokud řada procesů požaduje objekt z fondu současně, vytvoří fond všechny potřebné instance. Pak, pokud všechny procesy vrátí objekty zpět do fondu, ale Překontrolovat() nikdy nebude znovu zavolán, žádný z objektů nebude vyčištěn. U aktivních aplikací se jedná o vzácný výskyt, ale některé scénářové procesy, které mají „nečinný“ čas, mohou tento scénář vytvořit. Tento designový problém jsem vyřešil vláknem „clean up“, ale tuto diskusi si nechám pro druhou polovinu tohoto článku. Také se budu zabývat správným zpracováním chyb a šířením výjimek, aby byl fond robustnější pro kritické aplikace.

Thomas E. Davis je programátor Java s certifikací Sun. V současné době žije na slunné jižní Floridě, ale trpí jako workoholik a většinu času tráví uvnitř.

Tento příběh „Vytvořte si svůj vlastní ObjectPool v Javě, část 1“ původně publikoval JavaWorld.

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