views:

181

answers:

4

This blog post demonstrates a way to implement a mutex per string id idiom. The String ids used are to represent HttpSession ids.

  1. Why do we need to wrap a WeakReference around the Mutex instances ? Isn't it better to just create a Map from String -> Mutex ?
  2. Why do we need to call put twice ?

    public Mutex getMutex( String id )
    {                                  
        Mutex key = new MutexImpl( id );
        synchronized( mutexMap )
        {
            WeakReference<Mutex> ref = mutexMap.get( key );
            if( ref == null )
            {
                mutexMap.put( key, new WeakReference<Mutex>( key ) );
                return key;
            }
            Mutex mutex = ref.get();
            if( mutex == null )
            {
                mutexMap.put( key, new WeakReference<Mutex>( key ) );
                return key;
            }
            return mutex;
        }
    }
    
+4  A: 

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.

+1  A: 

I would say that the WeakReference's are used only because they need to refer to http session ids. You can not always be 100% sure that you get a notification that a session ends so that would cause an ever growing map.

The first time you call put it is because the map does not contain your key. The second time it is because the map did contain the key but the reference no longer existed.

Ronald Wildenberg
FYI: you can get notifications about the session state via HttpSessionListener and HttpSessionActivationListener. I wanted something that wasn't tied to the servlet API though and was easier to manage. That caveat aside, you're right.
McDowell
+2  A: 

1 - @Loop has a good answer.

2 - Assuming the entries are wrapped with WeakReferences, the second put is necessary because that WeakReference may be collected before the execution reaches the line:

 Mutex mutex = ref.get();

In this case:

  • the entry may not already exist in the map
  • if it exists, it may be collected before the execution of Mutex mutex = ref.get();
bruno conde
+1  A: 

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.

McDowell