RFC: Adding ConcurrentModificationException for HashMap.computeIfAbsent(), and JDK-8071667

Brent Christian brent.christian at oracle.com
Fri Feb 6 21:12:23 UTC 2015


On 2/5/15 12:46 AM, Paul Sandoz wrote:
> On Feb 5, 2015, at 1:36 AM, Brent Christian
> <brent.christian at oracle.com> wrote:
>> I prefer this approach of discouraging/preventing side-effects via
>> CME, rather than allowing them.
>> ...
>> Regarding the default methods:
>> Would we be able to make a "best-effort" detection of
>> comodification...
 >>
> My inclination is to leave these as is and focus on the most common
> concrete implementations.

Fair enough.  So, spec-wise...

For Map, I'd like to add some "words of discouragement" regarding 
modifying the map from function code.  Maybe also encourage implementing 
classes to detect and throw CME? (The default method will not do so.)

For HashMap: update the doc to say concurrent mods will results in a 
CME, add @throws.

For ConcurrentMap: It does not discourage modifying the map from 
function code, but it should IMO.  It should not encourage subclasses to 
throw CME.

ConcurrentHashMap: Does it need any changes?  It already mentions that 
one "must not attempt to update any other mappings of this map."  FWIW, 
giving the side-effecty mappingFunction from 8071667 to a CHM hangs for 
me; I've not investigated. :\

I don't think any changes are needed for concrete Map impls that inherit 
the default Map methods (e.g. TreeMap, IdentityHashMap)

Here's an initial rough cut, for computeIfAbsent():

diff -r 330dcd651f3b src/java.base/share/classes/java/util/Map.java
--- a/src/java.base/share/classes/java/util/Map.java    Mon Feb 02 
12:35:18 2015 -0800
+++ b/src/java.base/share/classes/java/util/Map.java    Fri Feb 06 
12:49:19 2015 -0800
@@ -925,6 +925,11 @@
       * }
       * }</pre>
       *
+     * <p>The mappingFunction itself should not make changes to this map.
+     * Implementing classes are encouraged to detect such modifications and
+     * throw ConcurrentModificationException. The default 
implementation does
+     * not do so.
+     *
       * <p>The default implementation makes no guarantees about 
synchronization
       * or atomicity properties of this method. Any implementation 
providing
       * atomicity guarantees must override this method and document its

diff -r 330dcd651f3b src/java.base/share/classes/java/util/HashMap.java
--- a/src/java.base/share/classes/java/util/HashMap.java        Mon Feb 
02 12:35:18 2015 -0800
+++ b/src/java.base/share/classes/java/util/HashMap.java        Fri Feb 
06 12:49:19 2015 -0800
@@ -1082,6 +1082,17 @@
          return null;
      }

+    /**
+     * {@inheritDoc}
+     *
+     * <p>The mappingFunction itself should not make changes to this map.
+     * If the function causes a structural modification to the map, a
+     * ConcurrentModificationException will be thrown.  As with 
iterators, this
+     * exception is thrown on a best-effort basis.
+     *
+     * @throws ConcurrentModificationException if a structural change was
+     * detected while executing the mappingFunction
+     */
      @Override
      public V computeIfAbsent(K key,
                               Function<? super K, ? extends V> 
mappingFunction) {

diff -r 330dcd651f3b 
src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
--- 
a/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java 
     Mon Feb 02 12:35:18 2015 -0800
+++ 
b/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java 
     Fri Feb 06 12:49:19 2015 -0800
@@ -305,6 +305,8 @@
       * threads attempt updates including potentially calling the mapping
       * function multiple times.
       *
+     * <p>The mappingFunction itself should not make changes to this map.
+     *
       * <p>This implementation assumes that the ConcurrentMap cannot 
contain null
       * values and {@code get()} returning null unambiguously means the 
key is


Thanks,
-Brent



More information about the core-libs-dev mailing list