Java References by Jonathan Amsterdam Listing One import java.lang.ref.*; public abstract class SoftObject { private SoftReference ref = new SoftReference(null); public Object get() throws Exception { Object result = ref.get(); if (result == null) { result = retrieve(); ref = new SoftReference(result); } return result; } protected abstract Object retrieve() throws Exception; } Listing Two import java.io.*; public class FileObject extends SoftObject { private String filename; FileObject(String fn) { filename = fn; } protected Object retrieve() throws IOException, ClassNotFoundException { ObjectInputStream in = new ObjectInputStream( new FileInputStream(filename)); try { return in.readObject(); } finally { in.close(); } } } Listing Three import java.util.*; import java.lang.ref.*; class Symbol { private String name; Object value; private static Map table = new HashMap(); private Symbol(String nm) { name = nm; } String getName() { return name; } static Symbol intern(String name) { Symbol s = (Symbol) table.get(name); if (s == null) { s = new Symbol(name); table.put(name, s); } return s; } } Listing Four static Symbol intern(String name) { Reference r = (Reference) table.get(name); Symbol s = null; if (r != null) s = (Symbol) r.get(); if (r == null || s == null) { s = new Symbol(name); table.put(name, new WeakReference(s)); } return s; } Listing Five import java.util.*; import java.lang.ref.*; /** This class is for maintaining canonical objects. */ public class CanonicalTable { private Map map = new HashMap(); private ReferenceQueue queue = new ReferenceQueue(); private Factory factory; public interface Factory { public Object create(Object key); } public CanonicalTable() {} public CanonicalTable(Factory f) { factory = f; } public synchronized Object canonicalize(Object key) { return canonicalize(key, null); } public synchronized Object canonicalize(Object key, Object o) { cleanup(); Object value = map.get(key); if (value != null) value = ((WeakReference) value).get(); if (value != null) return value; else { if (o == null) o = factory.create(key); map.put(key, new WeakValue(key, o, queue)); return o; } } public synchronized Object get(Object key) { cleanup(); Object value = map.get(key); if (value != null) return ((WeakReference) value).get(); else return null; } private void cleanup() { Reference r; while ((r = queue.poll()) != null) map.remove(((WeakValue) r).key); } //////// private static class WeakValue extends WeakReference { Object key; WeakValue(Object k, Object o, ReferenceQueue q) { super(o, q); key = k; } } } Listing Six import java.util.*; import java.lang.ref.*; /** A class for simplifying the use of phantom references. */ public class Cleanup { // Doubly linked list of CleanupReferences, with an empty header. private static CleanupReference list = new CleanupReference(); private static ReferenceQueue queue = new ReferenceQueue(); private static Thread backgroundThread = null; private static ArrayList exceptions = null; public interface Handler { public void cleanup() throws Exception; } /** Register a cleanup handler with an object. */ public static void register(Object o, Handler h) { synchronized (list) { CleanupReference r = new CleanupReference(o, h); r.linkAfter(list); } } /** Perform all pending cleanup operations. */ public static void doPending() throws Exception { Reference r; while ((r = queue.poll()) != null) ((CleanupReference) r).cleanup(); } /** Start a thread to do cleanup in the background. */ public static synchronized void startBackground() { if (backgroundThread != null) return; // already running backgroundThread = new Thread(new Runnable() { public void run() { while (!Thread.interrupted()) { try { CleanupReference r = (CleanupReference) queue.remove(); r.cleanup(); } catch (InterruptedException e) { // do nothing; loop will end } catch (Exception e) { addException(e); } } } }); backgroundThread.setPriority(Thread.MIN_PRIORITY); backgroundThread.start(); } /** Stop the background cleanup thread. */ public static synchronized void stopBackground() { if (backgroundThread != null) { backgroundThread.interrupt(); backgroundThread = null; } } /** Get a list of all exceptions generated by cleanup calls in the background thread. */ public static synchronized List getExceptions() { ArrayList result = exceptions; exceptions = null; return result; } private static synchronized void addException(Exception e) { if (exceptions == null) exceptions = new ArrayList(); exceptions.add(e); } //////////////////////////////////////////// private static class CleanupReference extends PhantomReference { private Handler handler; private CleanupReference next, prev; CleanupReference() { // Used only for head of linked list. // Queue is never garbage; ensures // no enqueuing. super(queue, queue); next = prev = this; } CleanupReference(Object o, Handler h) { super(o, queue); handler = h; } void linkAfter(CleanupReference c) { this.prev = c; this.next = c.next; c.next.prev = this; c.next = this; } void cleanup() throws Exception { try { handler.cleanup(); } finally { this.clear(); synchronized (list) { // unlink this.prev.next = this.next; this.next.prev = this.prev; } } } } } 5