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.