----- Original Message ----- > From: "Stuart Marks" <stuart.ma...@oracle.com> > To: "-" <liangchenb...@gmail.com>, "core-libs-dev" > <core-libs-dev@openjdk.java.net> > Sent: Saturday, March 5, 2022 1:58:30 AM > Subject: Re: Replace simple iterations of Map.entrySet with Map.forEach calls
> Hi, I have to say I'm pretty skeptical of this change overall. > > It sounds like the main issue is iterating over the entrySet of a map that > might > be > modified concurrently. The bug report says > > > for concurrent maps, regular iteration of entrySet may fail spontaneously > > This isn't true for ConcurrentHashMap or ConcurrentSkipListMap, which have > weakly > consistent iterators that won't error out. It *is* true for synchronizedMap, > which > can throw ConcurrentModificationException if the map is modified during > iteration. I > guess that's mostly what you're referring to. > > The problem with synchronizedMap (and the other synchronized wrappers) is that > they > require external synchronization around iteration, and it's pretty much > impossible > to cover all the cases where iteration occurs. It's used everywhere outside > the > JDK. > Even within the JDK, it appears quite frequently and is hard to avoid. For > example, > consider > > collection.addAll(synchronizedMap.keySet()) > > AbstractCollection::addAll iterates its argument, and this is inherited in a > bunch > of places. > > The root of the problem is that the synchronized wrappers support concurrent > updating only in a very narrow range of use cases. If people have trouble with > concurrent map updating, they need to use a real concurrent data structure, or > they > need to put everything inside their own class and have it do locking around > higher-level operations. Replacing a few entrySet iteration cases with > Map::forEach > doesn't help anything. Hi DrDeprecator, i wonder if we should not deprecate the method synchronized*, usually they cause more harm than good for the reason you explain. > > s'marks RĂ©mi > > > > On 2/22/22 3:09 PM, - wrote: >> Hello, >> In the patch for 8281631 <https://bugs.openjdk.java.net/browse/JDK-8281631>, >> xenoamess intentionally avoided >> <https://github.com/openjdk/jdk/pull/7431#discussion_r810666916> repeatedly >> calling Map::size, for it may not be constant-timed due to being >> concurrent. This alerted me that the for loops on Map::entrySet may be >> similarly not thread-safe. >> >> I believe that we should migrate for loop on Map::entrySet to Map::forEach >> calls; concurrent map callees usually have dedicated logic >> <https://github.com/openjdk/jdk/blob/2557ef8a02fe19784bd5e605b11d6bd574cde2c2/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java#L119> >> to mitigate such thread-safety issues. Synchronized map >> <https://github.com/openjdk/jdk/blob/2557ef8a02fe19784bd5e605b11d6bd574cde2c2/src/java.base/share/classes/java/util/Collections.java#L2735> >> callees are benefitted too. >> >> Another lesser benefit is reduced object allocation for iteration. While >> the most popular implementations (HashMap, LinkedHashMap, WeakHashMap, >> TreeMap) don't need to allocate entries for iteration, many other (EnumMap, >> IdentityHashMap, concurrent maps) do, and iterating those maps with forEach >> is less costly. (Note that 8170826 >> <https://bugs.openjdk.java.net/browse/JDK-8170826> takes care of >> implementing proper forEach in EnumMap) >> >> A JBS issue has already been submitted at >> https://bugs.openjdk.java.net/browse/JDK-8282178, and I have prepared a >> patch. This patch modifies the putAll implementations of a few JDK maps to >> let the callee feed entries through Map::forEach to be put into the maps. >> Two places of Map::entrySet calls in Collectors has also been updated. >> >> For most other existing usages in the JDK, I don't think they will benefit >> from such a migration: They mostly iterate on the entryset of the popular >> implementations that already host entries and are single-threaded, and I >> don't think it's in our best interest to touch those use cases. >> >> So, here are my questions: >> 1. Is such a concept of replacing Map::entrySet calls with Map::forEach >> calls reasonable, or is there any fundamental flaw? >> If the concept sounds good, I will submit a patch, so we can answer >> 2. Is the changes implemented correct for this concept, i.e. targeting code >> where users supply callee maps? >> >> Best regards