final ReferenceQueue
public ResourceManager() {
queue = new ReferenceQueue
// ... initialize resources ...
}
public synchronized void shutdown() {
if (!shutdown) {
shutdown = true;
reaper.interrupt();
}
}
public synchronized Resource getResource(Object key) {
if (shutdown)
throw new IllegalStateException();
Resource res = new ResourceImpl(key);
Reference<?> ref =
new PhantomReference
The key object can be an arbitrary objectthis gives great flexibility to the users of the resource, compared to
having the resource manager assign a key. When getresource is invoked, a new ResourceImpl object
is created, passing in the supplied key. A phantom reference is then created, with the key as the referent, and
using the resource manager's reference queue. The phantom reference and the resource object are then stored
into a map. This map serves two purposes: First, it keeps all the phantom reference objects reachable; second
it provides an easy way to find the actual resource object associated with each phantom reference. (The
alternative would be to subclass PhantomReference and store the Resource object in a field.)
The resource manager uses a separate "reaper" thread to process resources when the key has become
unreachable. The shutdown method "turns off" the resource manager by allowing the reaper to terminate (in
response to the interruption) and causing geTResource calls to throw IllegalStateException. In
this simple design, any references enqueued after shutdown will not be processed. The actual reaper thread
looks like this:
class ReaperThread extends Thread {
public void run() {
// run until interrupted
while (true) {
try {
Reference<?> ref = queue.remove();
Resource res = null;
synchronized(ResourceManager.this) {
res = refs.get(ref);
refs.remove(ref);
}
res.release();
ref.clear();
}
catch (InterruptedException ex) {