简体   繁体   中英

Consistency behaviour of ConcurrentHashMap.keySet().stream()

What consistency behaviour can I expect from this modify method with a ConcurrentHashMap ?

// map is filled concurrently from multiple threads
private final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();


public void modify(Object newValue) {
  map.keySet().stream()
    .filter(/* some filter */)
    .forEach(k -> {
      if (/* k has some property */) {
        map.put(k, newValue);
      } else {
        map.remove(k);
      }
    });
}

I find it difficult to get a definitive answer from reading the Javadocs, how keySet() , stream() , and modification of the map entry inside the stream interact together. I know that keySet() 's spliterator is weakly consistent , so the stream should at least iterate through all the elements, as they were present at some point in the past, only once. And that modifying the data source during stream execution is supported for concurrent collections (see Non-interference ). It might be relevant that I am always modifying the current key only and not touching other map entries in the foreach lambda.

So is the above code "all good" in a weakly consistent manner, or are there some caveats which I should be aware of?


Note: The above code also doesn't make really sense like that, but this is simplified code with other non-concurrency-related aspects left out.

It's possible that the code can be written better using different methods, eg forEach on the map itself. Feel free to suggest improvements if it adds to the answer, but please provide only answers that answer my actual question. I am not asking how to make this better, I am merely wondering how all the parts interact in my example.

the stream should at least iterate through all the elements, as they were present at some point in the past, only once

I would remove "all" from the phrase, because this wording might give a false impression that the stream iterates over some atomic snapshot of the whole ConcurrentHashMap .
In reality the stream might reflect (some) modifications of the map, which happened after the stream was created. As a result, it is possible that the first and the last keys emitted by a stream have never been in the map at the same time.

The wording for weakly consistent (referenced by the keySet()'s javadoc ) mentions that:

they [ie Iterators and Spliterators] are guaranteed to traverse elements as they existed upon construction exactly once, and may (but are not guaranteed to) reflect any modifications subsequent to construction.

It is also mentioned in another words in the javadoc for Traverser (the base class for iterators and spliterators) in ConcurrentHashMap :

Encapsulates traversal for methods such as containsValue; also serves as a base class for other iterators and spliterators.

Method advance visits once each still-valid node that was reachable upon iterator construction. It might miss some that were added to a bin after the bin was visited, which is OK wrt consistency guarantees.

Also "some point in the past" is pretty vague as well: you cannot see a key which was removed from the map before the stream() was created.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM