BewareMyPower commented on PR #23217:
URL: https://github.com/apache/pulsar/pull/23217#issuecomment-2309610378

   > Some map implementations don't have an atomic computeIfAbsent 
implementation
   
   A more accurate description is that the `ConcurrentSkipListMap` does not 
guarantee if `mappingFunction` argument is called the computed value must be 
inserted to the map.
   
   Given the following example:
   
   Thread 1:
   
   ```java
   final var result1 = map.computeIfAbsent("key", __ -> {
       System.out.println("value1");
       return "value1";
   });
   ```
   
   Thread 2:
   
   ```java
   final var result2 = map.computeIfAbsent("key", __ -> {
       System.out.println("value2");
       return "value2";
   });
   ```
   
   There is a case that both "value1" and "value2" are printed but `result1` 
and `result2` are the same ("value1" or "value2").
   
   However, it's expected because the Java Language Specification only 
guarantees the happens-before relationship on concurrent collections, see 
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html#Weakly
   
   > The methods of all classes in java.util.concurrent and its subpackages 
extend these guarantees to higher-level synchronization. In particular:
   > - Actions in a thread prior to placing an object into any concurrent 
collection happen-before actions subsequent to the access or removal of that 
element from the collection in another thread.
   
   Let's simplify and extend the example above. 
   
   Thread 1:
   
   ```java
   final var result1 = map.computeIfAbsent("key", __ -> "value1"); // A
   final var result3 = map.get("key"); // C
   ```
   
   Thread 2:
   
   ```java
   final var result2 = map.computeIfAbsent("key", __ -> "value2"); // B
   final var result4 = map.get("key"); // D
   ```
   
   There are only two possible cases for the `ConcurrentSkipListMap` (and 
`ConcurrentHashMap`): `value1` and `value2`. If it's `value1`, then we will 
have A happens-before B because the behavior is just like:
   1. A inserted "key -> value1" and succeeded
   2. A returned "value1" because it's inserted
   3. B inserted "key -> value2" and failed
   4. B returned the existing value "value1"
   
   The "concurrent" hash map only guarantees B could not return a value other 
than "value1". Because the last write operation before the read operation of B 
is A so B could only see "value1" written by A.
   
   Besides, the concurrent map only guarantees:
   - `result3` is "value1" because A happens-before C, A reads "value1" and 
there is no other write operation between A and C
   - `result4` is "value1" before B happens-before D, B reads "value1" and 
there is no other write operation between B and D


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to