V C++ existuje jedna velmi používaná technika pro získávání/uvolňování zdroje (zamykání, alokaci paměti, vrácení databázového spojení do connection poolu, apod.) – používají ji např. autopointery (auto_ptr<> a Smart Pointers z boost.org).
Základní myšlenkou je, že vytvoříte jednoduchý objekt na zásobníku. V jeho konstruktoru získáte zdroj a v destruktoru opět uvolníte. Celé je to velmi efektivní, protože dojde k alokaci paměti jen na zásobníku.
Kód vypadá takto:
if (true) { auto_ptr data(new Data()); data->delej(); } // zde jsou data již uvolněna.
Velkou výhodou tohoto způsobu je, že se nikdy nemusíte bát, zda jste zapomněli uvolnit zdroj. Překladač to za vás udělá sám. A při nahlédnutí do kódu vždy víte, že k uvolnění dojde. Toto je také jediný způsob jak bezpečně pracovat s pamětí v konstruktoru v C++ (viz. Exceptional C++).
Knihovna boost toto rozšiřuje i na reference counting „ukazatele“.
Vraťme se nyní k Javě. Přímá aplikace myšlenky nefunguje – v Javě není definováno kdy bude volán finalizer (obdoba destruktoru) a zda bude volán vůbec.
Fakt, že uvolňování zdroje je velmi důležité, ukazuje i příkaz synchronized:
synchronized (this) { // pracuj }
Možnou náhradou tedy je ruční volání s použitím finally
try { resource.lock(); // pracuj } finally { resource.unlock(); }
Oproti C++ variantě, ale můžeme něco přehlédnout, udělat chybu a na uvolnění prostě zapomenout. Příčinu problému pak můžeme hledat dlouho. Kolikrát jste hledali, kde se neuvolňuje spojení do databáze?
Nakonec nám tedy zbývá toto:
Resource.lock(resource, new Action() { public void run() { // pracuj } });
Před voláním metodu Action.run získáme zdroj a později opět uvolníme. Toto funkčností odpovídá variantě v C++ asi nejvíce, ale není tak elegantní a jednoduché.
Teď nám zbývá už jen doufat, že budoucí verze Javy přinese v tomto směru nějaké zlepšení. S podporou closures by kód mohl vypadat nějak takto:
Resource.lock(resource, { // pracuj });
nebo také:
protected (Connection = getConnection()) { // Connection implements Closable, so it's automatically closed at the end of this block // pracuj }
Pro tyto účely by byla hezké použití zpětné analýzy (escape analysis) pro alokaci paměti (v Javě 6 je již pro zamykání).
Na druhou stranu musím říct, že problém uvolňování zdrojů díky garbage collectoru a synchronized v Javě nenastává vůbec tak často. A těch několik míst, kde se získává spojení, si snadno ohlídáme.