Programavimas

Sukurkite savo „Java“ „ObjectPool“, 1 dalis

Objektų kaupimo idėja yra panaši į jūsų vietinės bibliotekos veikimą: kai norite skaityti knygą, žinote, kad pigiau skolintis kopiją iš bibliotekos, o ne įsigyti savo. Lygiai taip pat pigiau (atsižvelgiant į atmintį ir greitį) procesui skolintis o ne sukurti savo kopiją. Kitaip tariant, bibliotekoje esančios knygos atspindi objektus, o bibliotekos globėjai - procesus. Kai procesui reikalingas objektas, jis patikrina objektų telkinio kopiją, o ne sukuria naują. Tada procesas grąžina objektą į baseiną, kai jis nebereikalingas.

Tačiau tarp objektų kaupimo ir bibliotekos analogijos yra keletas nedidelių skirtumų, kuriuos reikėtų suprasti. Jei bibliotekos globėjas nori konkrečios knygos, tačiau visos tos knygos egzemplioriai yra patikrinami, globėjas turi palaukti, kol bus grąžinta jų kopija. Mes niekada nenorime, kad procesas turėtų laukti objekto, todėl objektų telkinys prireikus iš karto sugeneruos naujas kopijas. Tai gali sukelti nepaprastai daug objektų, gulinčių aplink baseiną, todėl taip pat bus suskaičiuoti nenaudojami daiktai ir periodiškai juos išvalyti.

Mano objektų telkinio dizainas yra pakankamai bendras, kad būtų galima tvarkyti saugojimo, stebėjimo ir galiojimo laiką, tačiau specifinių objektų tipų eksponavimas, patvirtinimas ir sunaikinimas turi būti atliekamas per klasę.

Dabar, kai pagrindai iš kelio, leidžia pereiti į kodą. Tai yra griaučių objektas:

 public abstract class ObjectPool {private long expirationTime; privatus „Hashtable“ užrakintas, atrakintas; abstraktus Objekto sukūrimas (); abstraktus loginis patvirtinimas (objektas o); abstrakti tuštuma pasibaigia (objektas o); sinchronizuotas „Object checkOut“ () {...} sinchronizuotas „void checkIn“ („Object o“) {...}} 

Vidinis sujungtų objektų saugojimas bus tvarkomas dviem „Hashtable“ objektai, vienas skirtas užrakintiems, o kitas - atrakintiems. Patys objektai bus „hashtable“ raktai, o jų paskutinio naudojimo laikas (epochos milisekundėmis) bus vertė. Saugodamas paskutinį kartą, kai objektas buvo naudojamas, baseinas gali jį naudoti ir atlaisvinti atmintį po tam tikros trukmės neveikimo.

Galų gale objektų telkinys leistų poklasiui nurodyti pradinį „hashtable“ dydį, jų augimo greitį ir galiojimo laiką, tačiau aš stengiuosi, kad šio straipsnio tikslais būtų paprasta, sunkiai užkoduodamas šias reikšmes konstruktorius.

 ObjectPool () {expirationTime = 30000; // 30 sekundžių užrakinta = new Hashtable (); atrakinta = new Hashtable (); } 

checkout () metodas pirmiausia patikrina, ar atrakintoje „hashtable“ nėra objektų. Jei taip, jis peržiūri juos ir ieško tinkamo. Patvirtinimas priklauso nuo dviejų dalykų. Pirma, objektų telkinys patikrina, ar objekto paskutinio naudojimo laikas neviršija poklasyje nurodyto galiojimo laiko. Antra, objektų telkinys vadina abstraktųjį patvirtinti () metodas, kuris atlieka bet kurios klasės patikrinimą ar pakartotinį inicijavimą, kurio reikia norint pakartotinai naudoti objektą. Jei objekto patvirtinti nepavyksta, jis atlaisvinamas ir kilpa eina į kitą objektą grotelėse. Kai randamas objektas, kuris praeina patvirtinimą, jis perkeliamas į užrakintą „hashtable“ ir grąžinamas į procesą, kurio buvo prašoma. Jei atrakinta „hashtable“ yra tuščia arba nė vienas iš jos objektų nepatvirtina patvirtinimo, naujas objektas yra supaprastintas ir grąžinamas.

 sinchronizuotas objekto tikrinimas () {long now = System.currentTimeMillis (); Objektas o; if (unlocked.size ()> 0) {Surašymas e = unlocked.keys (); o (e.hasMoreElements ()) {o = e.nextElement (); if ((dabar - ((Long) unlocked.get (o)) .longValue ())> expirationTime) {// objektas pasibaigęs unlocked.remove (o); pasibaigti (o); o = nulis; } else {if (patvirtinti (o)) {atrakinta.pašalinti (o); locked.put (o, naujas Ilgas (dabar)); grįžimas (o); } else {// objekto patvirtinimo nepavyko atrakinti.remove (o); pasibaigti (o); o = nulis; }}}} // objektų nėra, sukurkite naują o = create (); locked.put (o, naujas Ilgas (dabar)); grįžimas (o); } 

Tai pats sudėtingiausias metodas „ObjectPool“ klasės, viskas čia nuo kalno. įsiregistruoti() metodas paprasčiausiai perkelia perduodamą objektą iš užrakto hashtable į atrakintą hashtable.

sinchronizuotas tuštumos patikrinimas (Object o) {locked.remove (o); unlocked.put (o, naujas Long (System.currentTimeMillis ())); } 

Trys likę metodai yra abstraktūs, todėl juos turi įgyvendinti poklasis. Dėl šio straipsnio ketinu sukurti duomenų bazės ryšio telkinį, vadinamą „JDBCConnectionPool“. Štai skeletas:

 viešoji klasė „JDBCConnectionPool“ pratęsia „ObjectPool“ {privačią eilutę dsn, usr, pwd; public JDBCConnectionPool () {...} create () {...} validate () {...} expire () {...} public Connection borrowConnection () {...} public void returnConnection () {. ..}} 

„JDBCConnectionPool“ pareikalavus iš programos (per konstruktorių) programai reikės nurodyti duomenų bazės tvarkyklę, DSN, vartotojo vardą ir slaptažodį. (Jei jums visa tai graikų kalba, nesijaudinkite, JDBC yra kita tema. Tiesiog laikykitės manęs, kol grįšime prie susikaupimo.)

 public JDBCConnectionPool (String driver, String dsn, String usr, String pwd) {išbandykite {Class.forName (tvarkyklė) .newInstance (); } sugavimas (e išimtis) {e.printStackTrace (); } tai.dsn = dsn; tai.usr = usr; tai.pwd = pwd; } 

Dabar galime pasinerti į abstrakčių metodų įgyvendinimą. Kaip matėte checkout () metodas, „ObjectPool“ iškvies „create“) iš savo poklasio, kai reikės sukurti naują objektą. Dėl „JDBCConnectionPool“, mes turime tik sukurti naują Ryšys daiktą ir perduoti atgal. Vėlgi, norėdamas, kad šis straipsnis būtų paprastas, išmetu atsargumą vėjui ir nepaisau jokių išimčių ir nulinio rodiklio sąlygų.

 Object create () {try {return (DriverManager.getConnection (dsn, usr, pwd)); } gaudyti (SQLException e) {e.printStackTrace (); grįžti (nulinis); }} 

Prieš „ObjectPool“ išlaisvina objektą, kurio galiojimo laikas yra pasibaigęs (arba netinkamą) šiukšlių surinkimui, jis perduoda jį savo poklasiui pasibaigti () būtino paskutinės minutės valymo metodas (labai panašus į baigti () metodas, kurį iškvietė šiukšlių surinkėjas). Jeigu „JDBCConnectionPool“, mums tereikia uždaryti ryšį.

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

Galiausiai turime įdiegti patvirtinimo () metodą „ObjectPool“ skambina norėdami įsitikinti, kad objektą vis dar galima naudoti. Čia taip pat turėtų vykti bet koks pakartotinis inicijavimas. Dėl „JDBCConnectionPool“, mes tik patikriname, ar ryšys vis dar atidarytas.

 loginis patvirtinimas (Object o) {try {return (! ((Connection) o) .isClosed ()); } gaudyti (SQLException e) {e.printStackTrace (); grįžti (klaidinga); }} 

Tai viskas dėl vidinio funkcionalumo. „JDBCConnectionPool“ leis programai skolintis ir grąžinti duomenų bazės ryšius šiais neįtikėtinai paprastais ir tinkamai įvardintais metodais.

 public Connection borrowConnection () {return ((Connection) super.checkOut ()); } public void returnConnection (Ryšys c) {super.checkIn (c); } 

Šis dizainas turi porą trūkumų. Bene didžiausia yra galimybė sukurti didelį objektų baseiną, kuris niekada nebus paleistas. Pavyzdžiui, jei krūva procesų vienu metu prašo objekto iš telkinio, baseinas sukurs visas būtinas egzempliorius. Tada, jei visi procesai grąžina objektus atgal į baseiną, bet checkout () daugiau niekada nebeskambina, nė vienas objektas neišvalomas. Tai retas atvejis aktyviose programose, tačiau kai kurie vidiniai procesai, turintys „neveikos“ laiką, gali sukurti šį scenarijų. Aš išsprendžiau šią dizaino problemą „išvalymo“ gija, tačiau šią diskusiją išsaugosiu antroje šio straipsnio pusėje. Taip pat apžvelgsiu tinkamą klaidų tvarkymą ir išimčių sklaidą, kad telkinys būtų patikimesnis kritiškai svarbioms programoms.

Thomas E. Davisas yra „Sun“ sertifikuotas „Java“ programuotojas. Šiuo metu jis gyvena saulėtoje Pietų Floridoje, tačiau kenčia kaip darboholikas ir didžiąją laiko dalį praleidžia uždarose patalpose.

Šią istoriją „Sukurkite savo„ ObjectPool “sistemoje„ Java “, 1 dalis“ iš pradžių paskelbė „JavaWorld“.