ConcurrentHashMap::computeIfAbsent and synchronized

Doug Lea dl at cs.oswego.edu
Fri Jan 20 12:51:17 UTC 2023


Updates to ConcurrentHashMap to avoid builtin synchronization are on the 
todo list. As you've noticed, using a ReentrantLock per node would add 
unacceptable overhead, and would be especially unwelcome for the huge 
number of non-loom usages. I have a few ideas in mind, but it will 
surely be a while until arriving at a replacement good enough to release.

-Doug

On 1/20/23 02:40, Dr Heinz M. Kabutz wrote:
>
> Hi Mika,
>
> I was thinking about that yesterday. Since CHM gives a guarantee that 
> the compute() functions are called atomically, it might be worth 
> looking at the implications of moving (back) to ReentrantLock. 
> However, that would also mean that we need to allocate a lot of 
> ReentrantLock instances, and those are 40 bytes per lock. Here is a 
> demo that shows the issue:
>
> import java.util.Map; import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; public class CHMPinning {
>      public static void main(String... args)throws InterruptedException {
>          Map<Integer, Integer> map =new ConcurrentHashMap<>(); for (int i =0; i <1_000; i++) {
>              int finalI = i; Thread.startVirtualThread(() ->
>                      map.computeIfAbsent(finalI %3, key -> {
>                          try {
>                              Thread.sleep(2_000); }catch (InterruptedException e) {
>                              throw new CancellationException("interrupted"); }
>                          return finalI; })); }
>          long time = System.nanoTime(); try {
>              Thread.startVirtualThread(() ->
>                              System.out.println(
>                                      "Hi, I'm an innocent virtual thread"))
>                      .join(); }finally {
>              time = System.nanoTime() - time; System.out.printf("time = %dms%n", (time /1_000_000)); }
>          System.out.println("map = " + map); }
> }
>
> Output is something like:
>
> Hi, I'm an innocent virtual thread
> time = 2002ms
> map = {0=0, 1=7, 2=2}
>
> IMHO, I would not like to see the CHM memory usage increase by 40 x # 
> nodes bytes to cater for an edge case of the compute() function taking 
> a bit longer.
>
>
> Regards
>
> Heinz
> -- 
> Dr Heinz M. Kabutz (PhD CompSci)
> Author of "The Java™ Specialists' Newsletter" -www.javaspecialists.eu
> Java Champion -www.javachampions.org
> JavaOne Rock Star Speaker
> Tel: +30 69 75 595 262
> Skype: kabutz
> On 2023/01/20 01:22, mikmoila wrote:
>> Hi.
>>
>> As often mentioned in this mailing-list a feedback about 
>> preview/incubator features is appreciated, so here's one:
>>
>> I was experimenting with a caching system utilising ConcurrentHashMap 
>> as cache store and Structured Concurrency API for refreshing the 
>> entries from multiple sources ( StructuredTaskScope.ShutdownOnSuccess 
>> ). The idea was to make http-requests for getting the fresh values 
>> but the first implementation simply uses UUID::randomUUID for 
>> simulating that.
>> I noticed that the programs halts In a test case where "N" concurrent 
>> calls (where "N" >= number of cpu cores) running on virtual threads 
>> end-up calling the ConcurrentHashMap::computeIfAbsent for the same 
>> (non-existing) key.
>>
>> "-Djdk.tracePinnedThreads=full" reveals that there is a pinned 
>> carrier thread:
>> java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) 
>> <== monitors:1
>>
>> The documentation ( 
>> https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ConcurrentHashMap.html#computeIfAbsent(K,java.util.function.Function) 
>> ) says:
>>
>>   "Some attempted update operations on this map by other threads may 
>> be blocked while computation is in progress, so the computation 
>> should be short and simple."
>>
>> This is clear but I still found it as a surprise that it uses 
>> synchronized instead of "virtual-thread friendly" constructs.
>>
>> If needed I can post a small demo program.
>>
>> JDK used is latest OpenJDK-19 GA, OS is Windows:
>>   openjdk version "19.0.2" 2023-01-17
>>   OpenJDK Runtime Environment (build 19.0.2+7-44)
>>   OpenJDK 64-Bit Server VM (build 19.0.2+7-44, mixed mode, sharing)
>>
>> Best Regards,
>>   Mika
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20230120/89ca977e/attachment-0001.htm>


More information about the loom-dev mailing list