Programiranje

Zgradite svoj ObjectPool v Javi, 1. del

Ideja združevanja objektov je podobna delovanju vaše lokalne knjižnice: ko želite prebrati knjigo, veste, da je ceneje izposoditi kopijo iz knjižnice, kot pa kupiti lastno kopijo. Prav tako je ceneje (glede na pomnilnik in hitrost) postopek sposoditi si predmeta, ne pa ustvariti lastne kopije. Z drugimi besedami, knjige v knjižnici predstavljajo predmete, pokrovitelji knjižnice pa procese. Ko proces potrebuje objekt, namesto novega ustvari kopijo iz področja predmetov. Nato postopek vrne objekt v področje, ko ni več potreben.

Obstaja pa nekaj manjših razlik med združevanjem objektov in analogijo knjižnice, ki bi jo bilo treba razumeti. Če pokrovitelj knjižnice želi določeno knjigo, vendar so vse kopije te knjige odjavljene, mora pokrovitelj počakati, da se kopija vrne. Nikoli si ne želimo, da bi proces moral čakati na objekt, zato bo področje predmetov po potrebi ustvarilo nove kopije. To bi lahko privedlo do izredno velike količine predmetov, ki ležijo v bazenu, zato bo tudi spremljal neuporabljene predmete in jih redno čistil.

Zasnova mojega področja predmetov je dovolj generična, da obvladuje čase shranjevanja, sledenja in izteka, vendar je treba instanciranje, preverjanje veljavnosti in uničenje določenih vrst predmetov obdelati s podrazvrstitvijo.

Zdaj, ko osnove niso na poti, skoči v kodo. To je skeletni objekt:

 javni abstraktni razred ObjectPool {private long expirationTime; zasebna Hashtable zaklenjena, odklenjena; povzetek Object create (); abstraktna logična potrditev (objekt o); abstraktna praznina poteče (predmet o); synchronized Object checkOut () {...} sinhronizirana void prijava (Object o) {...}} 

Notranji pomnilnik združenih predmetov bo obdelan z dvema Hashtable predmeti, eden za zaklenjene predmete in drugi za odklenjena. Predmeti sami bodo ključi tabele, njihov čas zadnje uporabe (v epoha milisekundah) pa bo vrednost. Če shrani zadnji objekt, ko je bil predmet uporabljen, mu lahko potek poteče in sprosti pomnilnik po določenem trajanju neaktivnosti.

Končno bi področje predmetov podrazredu omogočilo, da določi začetno velikost hashtables, skupaj s hitrostjo rasti in časom veljavnosti, vendar poskušam za namene tega članka narediti to preprosto tako, da te vrednosti trdo kodiram v konstruktor.

 ObjectPool () {expirationTime = 30000; // 30 sekund zaklenjeno = nova Hashtable (); odklenjeno = nova Hashtable (); } 

The preveri() metoda najprej preveri, ali so v odklenjeni hashtable-i predmeti. Če je tako, jih kroži in išče veljavnega. Validacija je odvisna od dveh stvari. Najprej področje predmetov preveri, ali čas zadnje uporabe predmeta ne presega časa poteka, določenega v podrazredu. Drugič, področje predmetov pokliče abstraktno potrdi () metoda, ki opravi kakršno koli preverjanje ali vnovično inicializacijo, potrebno za ponovno uporabo predmeta. Če objekt ne uspe preveriti veljavnosti, se sprosti in zanka nadaljuje do naslednjega predmeta v razpredelnici. Ko najdete predmet, ki opravi preverjanje veljavnosti, se premakne v zaklenjeno razpršilno tablico in vrne v postopek, ki ga je zahteval. Če je odklenjena hashtable prazna ali noben njen objekt ne opravi preverjanja veljavnosti, se ustvari primerek novega predmeta in se vrne.

 sinhroniziran objekt checkOut () {long now = System.currentTimeMillis (); Predmet o; if (unlocked.size ()> 0) {Enumeration e = unlocked.keys (); while (e.hasMoreElements ()) {o = e.nextElement (); if ((zdaj - ((Long) unlocked.get (o)) .longValue ())> expirationTime) {// objekt je potekel unlocked.remove (o); poteče (o); o = nič; } else {if (validate (o)) {unlocked.remove (o); locked.put (o, novo Long (zdaj)); vrnitev (o); } else {// objekt ni uspel preveriti veljavnosti unlocked.remove (o); poteče (o); o = nič; }}}} // ni na voljo nobenega predmeta, ustvari novega o = create (); locked.put (o, novo Long (zdaj)); vrnitev (o); } 

To je najbolj zapletena metoda v ObjectPool razred, od tu je vse navzdol. The Prijava() metoda preprosto premakne predani predmet iz zaklenjene zgoščevalne tabele v odklenjeno razpredelnico.

sinhronizirana void prijava (objekt o) {zaklenjeno.odstranite (o); unlocked.put (o, novo Long (System.currentTimeMillis ())); } 

Preostale tri metode so abstraktne in jih je zato treba izvajati v podrazredu. Zaradi tega članka bom ustvaril povezavo baze podatkov z imenom JDBCConnectionPool. Tu je okostje:

 javni razred JDBCConnectionPool razširja ObjectPool {zasebni niz dsn, usr, pwd; public JDBCConnectionPool () {...} create () {...} validate () {...} expire () {...} public Connection borrowConnection () {...} public void returnConnection () {. ..}} 

The JDBCConnectionPool bo zahteval, da aplikacija določi gonilnik baze podatkov, DSN, uporabniško ime in geslo ob instanci (prek konstruktorja). (Če je za vas vse to grško, ne skrbite, JDBC je druga tema. Samo potrpite, dokler se ne vrnemo k združevanju.)

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

Zdaj se lahko poglobimo v izvajanje abstraktnih metod. Kot ste videli v preveri() metoda, ObjectPool bo poklical create () iz svojega podrazreda, ko bo moral ustvariti primerek novega predmeta. Za JDBCConnectionPool, vse, kar moramo storiti, je ustvariti novo Povezava predmet in ga vrnite nazaj. Še enkrat, da bi bil ta članek enostaven, previdno prenašam veter in ignoriram kakršne koli izjeme in ničelne pogoje.

 Predmet create () {try {return (DriverManager.getConnection (dsn, usr, pwd)); } catch (SQLException e) {e.printStackTrace (); vrnitev (null); }} 

Pred ObjectPool sprosti potekel (ali neveljaven) predmet za zbiranje smeti, ga preda svojemu podrazvrščenemu poteče () za kakršno koli potrebno čiščenje v zadnjem trenutku (zelo podobno dokončaj () metoda, ki jo pokliče zbiralec smeti). V primeru JDBCConnectionPool, vse kar moramo storiti je, da zapremo povezavo.

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

In na koncu moramo izvesti metodo validate () that ObjectPool pokliče, da se prepriča, ali je predmet še vedno veljaven za uporabo. To je tudi kraj, kjer naj se izvede kakršna koli ponovna inicializacija. Za JDBCConnectionPool, samo preverimo, ali je povezava še vedno odprta.

 logična validacija (Objekt o) {try {return (! ((Connection) o) .isClosed ()); } catch (SQLException e) {e.printStackTrace (); vrnitev (napačno); }} 

To je to za notranjo funkcionalnost. JDBCConnectionPool bo aplikaciji omogočil izposojo in vrnitev povezav do baze podatkov s pomočjo teh neverjetno preprostih in primerno imenovanih metod.

 javna povezava borrowConnection () {return ((Connection) super.checkOut ()); } public void returnConnection (Povezava c) {super.checkIn (c); } 

Ta zasnova ima nekaj pomanjkljivosti. Morda največja je možnost ustvarjanja velikega nabora predmetov, ki se nikoli ne sprostijo. Če na primer kup procesov hkrati zahteva objekt iz področja, bo bazen ustvaril vse potrebne primerke. Nato, če vsi procesi vrnejo predmete nazaj v področje, vendar preveri() nikoli več ne pokliče, noben predmet se ne očisti. To je redek pojav pri aktivnih aplikacijah, vendar lahko nekateri scenariji, ki imajo "nedejaven" čas, povzročijo ta scenarij. To težavo pri oblikovanju sem rešil z nitjo "clean up", vendar bom to razpravo shranil za drugo polovico tega članka. Zajemal bom tudi pravilno ravnanje z napakami in razširjanje izjem, da bo združenje bolj trpežno za kritične aplikacije.

Thomas E. Davis je programer Java, certificiran za sonce. Trenutno prebiva na sončni južni Floridi, vendar trpi kot deloholik in večino časa preživi v zaprtih prostorih.

To zgodbo "Sestavite svoj ObjectPool v Javi, 1. del" je prvotno objavil JavaWorld.

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