hg: lambda/lambda/jdk: bug fixes and unit test for Map Defaults

Sam Pullara spullara at gmail.com
Mon Mar 25 16:22:17 PDT 2013


Is there some reason it doesn't call replace() when the old value is null? Calling put seems wrong to me.

Sam

On Mar 25, 2013, at 2:57 PM, Peter Levart <peter.levart at gmail.com> wrote:

> Hi Mike,
> 
> I see default Map.computeIfAbsent has been chosen to not be atomic and 
> rather support null values more naturally. That's ok if JDK 
> ConcurrentMap implementations provide atomic overrides. Other 3rd party 
> implementations will follow soon.
> 
>  But I have doubts about default Map.compute(). On one hand it contains 
> the usual disclaimer:
> 
>       900      * <p>The default implementation makes no guarantees about
>       901      * synchronization or atomicity properties of this method 
> or the
>       902      * application of the remapping function. Any class 
> overriding
>       903      * this method must specify its concurrency properties.  In
>       904      * particular, all implementations of subinterface {@link
>       905      * java.util.concurrent.ConcurrentMap} must document 
> whether the
>       906      * function is applied exactly once atomically. Any class 
> that
>       907      * permits null values must document whether and how this 
> method
>       908      * distinguishes absence from null mappings.
> 
> ...but on the other hand it tries to be smart:
> 
>       924      * In concurrent contexts, the default implementation may 
> retry
>       925      * these steps when multiple threads attempt updates.
> 
> Now this last sentence may indicate that a ConcurrentMap implementation 
> that does not override compute() might safely be used with default 
> Map.compute() and be atomic. It was atomic until putIfAbsent() was 
> replaced with plain put() to correct the "existing null value" behavior. 
> The retry-loop is only needed when the optimistic operation is not 
> successful. But put() is not optimistic. It always "succeeds". And 
> retrying after the put() only makes things worse: "Oh, somebody was 
> quicker than me and I have just overwritten his value - never mind, I'll 
> try to make some more damage in next loop..."
> 
> If the damage was done already, then there's no point in repeating the 
> loop. Further, what's the point in using optimistic operations: 
> replace(key, oldValue, newValue) and remove(key, oldValue) with 
> retry-loop on one hand and then just stomping over with put() on the 
> other hand. If the default Map.compute() method is declared non-atomic, 
> then plain put(key, newValue) instead of replace(key, oldValue, 
> newValue) and remove(key) instead of remove(key, oldValue) could be used 
> and no retry-loop...
> 
> The same goes for default Map.merge().
> 
> Regards, Peter
> 
> P.S. What do you think about changing the specification of putIfAbsent 
> to always overwrite null values? It could make all these things simpler 
> and more consistent. And it would not break anything.
> 
> 
> On 03/25/2013 08:21 PM, mike.duigou at oracle.com wrote:
>> Changeset: c8d40b7e6de3
>> Author:    mduigou
>> Date:      2013-03-20 20:32 -0700
>> URL:       http://hg.openjdk.java.net/lambda/lambda/jdk/rev/c8d40b7e6de3
>> 
>> bug fixes and unit test for Map Defaults
>> 
>> ! src/share/classes/java/util/HashMap.java
>> ! src/share/classes/java/util/Map.java
>> + test/java/util/Map/Defaults.java
>> 
>> 
> 
> 



More information about the lambda-dev mailing list