Thanks for the detailed reply. Yes, JDK code is special. From an ordering perspective, the segmentAt() does a volatile read and ensures visibility. However from happen-before perspective, I cannot find happen-before relation between seg.count read action with seg.count write action.
thread 1 thread 2 -------------------------------------------------------------------------------------- 1. segment reference volatile write | 3. segment reference volatile read 2. seg.count write | 4. seg.count read It's obvious that action 1 has a happen-before relation with action 3. But there's no happen-before relation between action 2 and action 4. Am I wrong? Or I just should not consider it from the happen-before perspective because the JDK code is special? On Monday, September 18, 2017 at 11:31:52 PM UTC+8, Gil Tene wrote: > > In the presence of concurrent modification, size() can be stale by > definition, since the modification could occur between size() establishing > a notion of what size is and your next operation that might assume the size > is somehow representative of the current state of the table. > > When no concurrent modification exists between your call into size() and > whatever the next thing you do that depends on that size is, the size will > be up-to-date. The thing that achieves that in the OpenJDK 7 variant > <http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/util/concurrent/ConcurrentHashMap.java#ConcurrentHashMap.size%28%29> > > is the fact that segmentAt() does a volatile read, which is sufficient (at > least in the specific OpenJDK 7 JVM implementation) to establish a LoadLoad > ahead of the following reads of seg.count and seg.modCount to reflect > external modifications that (by other ordering rules) occurred prior to > your size() call. The unlock() at e.g. the bottom of seg.put() creates > enough ordering on the modifying side after the changes to seg.count and > seg.modCount. > > A critical thing to keep in mind when reading JDK code like this is that > the you should not necessarily assume that your code can do the same thing > safely from an ordering perspective. JDK code is special because it *knows* > what JVMs it ships with, and can validly make assumptions about the JVMs > behavior. Since j.u.c.ConcurrentHashMap in OpenJDK 7 is part of the JDK, it > can make certain assumptions about the OpenJDK 7 JVM's handling of ordering > that may be stronger than the JMM guarantees. E.g. it could assume that the > *sufficient* (but not fully required by the JMM) ordering rules in > http://gee.cs.oswego.edu/dl/jmm/cookbook.html are actually implemented, > based on knowledge of the specific JVM that ships with the JDK code. E.g. a > JVM that satisfies these rules: > > > <https://lh3.googleusercontent.com/--z6nET8Y_rI/Wb_j4Ym4AHI/AAAAAAAAATM/9hYYa3Llne0ibtkz4B1jm_7VZU-vXYI-wCLcBGAs/s1600/JMMCookbookTable.png> > > Will meet the JMM ordering requirements between volatile loads and stores, > regular loads and stores, and monitor enters and exits. But that doesn't > actually mean that all JVMs (and future JDKs you may run on) will actually > follow these rules. They may find some way to meet the JMM requirements > without following these rules to the letter. E.g. they may apply the > ordering between specific sets of fields (rather than globally, across all > fields as stated in this matrix), and still meet the JMM without enforcing > a LoadLoad between any volatile load and any field load. > > > On Sunday, September 17, 2017 at 11:54:45 PM UTC-7, yang liu wrote: >> >> "...useful only when a map is not undergoing concurrent updates in other >> threads..." >> The size() method sums the segment field "modCount" twice and compares >> the result to ensure no concurrent updates in other threads. >> If there's concurrent updates, the size() method resort to locking >> segments. So the size() method tries to get the mostly updated >> result even if after the result returns the result may already be stale. >> In java1.6 field "count" has volatile present and "modCount" >> field read has happen-before relation with "count" write, so the sum of >> "count" can has mostly updated result. But that's not the case for java 1.7. >> In java1.7 the no volatile present field "modCount" and "count" may fail >> to get mostly updated value. >> >> On Monday, September 18, 2017 at 1:46:34 PM UTC+8, Nikolay Tsankov wrote: >>> >>> * Bear in mind that the results of aggregate status methods including >>> * {@code size}, {@code isEmpty}, and {@code containsValue} are typically >>> * useful only when a map is not undergoing concurrent updates in other >>> threads. >>> * Otherwise the results of these methods reflect transient states >>> * that may be adequate for monitoring or estimation purposes, but not >>> * for program control. >>> >>> >>> On Mon, Sep 18, 2017 at 5:18 AM, yang liu <[email protected]> wrote: >>> >>>> Recently I studied the source code of ConcurrentHashMap. >>>> >>>> In java 1.7, the segment field "count" got no volatile modifiers which >>>> is different from java 1.6. >>>> >>>> Is possible the size() method could read stale value through race read? >>>> >>>> >>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "mechanical-sympathy" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to [email protected]. >>>> For more options, visit https://groups.google.com/d/optout. >>>> >>> >>> -- You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
