Loop and Bruno Conde have pretty much covered it, but since I wrote that code...
The design goal was to avoid having the user call a release mechanism - the mutex is eligible for garbage collection when the user no longer references it.
Why do we need to wrap a WeakReference
around the Mutex instances ?
The map is a WeakHashMap:
private final Map mutexMap = new WeakHashMap();
This map is used to keep a reference to the mutex, but if you use the same object for the key and the value, the object is not eligible for garbage collection. Javadoc:
Implementation note: The value objects
in a WeakHashMap are held by ordinary
strong references. Thus care should be
taken to ensure that value objects do
not strongly refer to their own keys,
either directly or indirectly, since
that will prevent the keys from being
discarded. Note that a value object
may refer indirectly to its key via
the WeakHashMap itself; that is, a
value object may strongly refer to
some other key object whose associated
value object, in turn, strongly refers
to the key of the first value object.
One way to deal with this is to wrap
values themselves within
WeakReferences before inserting, as
in: m.put(key, new
WeakReference(value)), and then
unwrapping upon each get.
Isn't it
better to just create a Map from
String -> Mutex ?
When will that String value be garbage collected? Is the same reference being passed every time? Has intern() been called on it? If I call intern, how long will the String live? If a String was the key, the mutex might not be eligible for garbage collection long after there is no need to keep a reference to it.
Why do we need to call put twice ?
There are two cases to handle until the method can get a strong reference to the mutex in the map:
- the WeakReference has been garbage collected (or was never there in the first place)
- the contents of the WeakReference are garbage collected after the reference to it has been acquired
put will only ever be invoked once; the method returns immediately after.
(The WeakReference could be reused in the second put, but I don't see that it would be a significant improvement.)
Of course, if anyone can find fault in the code, let me know and I'll happily correct it. Also, the unit tests try to check that the implementation doesn't leak, so feel free to modify the code and see what happens when you run the tests.